posts - 25,  comments - 396,  trackbacks - 35
    在上一篇文章也谈WEB打印(三):抛开IE,实现我们自己的打印模板中,我们写了一个自己的打印模板,然而,该模板并不支持打印,也只能显示2个页面。在本文,我们继续完善该模板,以让他支持打印,并且可以根据被打印的内容动态的生成页面。

   废话少说,我们先来分析这个模板:
<!-- Template2.htm: 一个很小的模板,它支持打印
这个模板展示了一个最小的打印模板,他可以打印我们在预览时见到的内容。本模板与Template1.htm完全相同,只是增加了TEMPLATEPRINTER元素和一些支持打印的脚本。这些脚本以函数CheckPrint开始。该函数处理第二个LAYOUTRECT元素的onlayoutcomplete事件,由一个时间是100毫秒的定时器触发。CheckPrint检查dialogArguments.__IE_PrintType 属性来决定是否是预览文档,或者是在打印之前需要以一个打印对话框来提示用户,还是不提示用户而直接打印。使用定时器来延时非常重要,因为设备上下文(DC)在onlayoutcomplete时间没有完成之前,不能渲染打印机或者屏幕。延时让使得打印发生在onlayoutcomplete事件完成后再开始。1毫秒就足够让onlayoutcomplete时间完成,但为了安全起见,在本例中,把定时器设为100毫秒。
PrintPrep是一个重要的函数,因为设置了一个onreadystatechange处理函数,以确定模板的源文档在打印之前被完全装入。
-->

<HTML XMLNS:IE>
<HEAD>
<?IMPORT NAMESPACE="IE" IMPLEMENTATION="#default">
<STYLE TYPE="text/css">
.lorstyle
{
    width:5.5in;
    height:8in;
    margin:1in;
    background:white;   
    border:1 dashed gray;
}
.pagestyle

    width:8.5in;
    height:11in;
    background:#FFFF99;   
    border-left:1 solid black;
    border-top:1 solid black;
    border-right:4 solid black;
    border-bottom:4 solid black;
    margin:10px;
}
</STYLE>
<SCRIPT LANGUAGE="JScript">
function CheckPrint()
{
     switch (dialogArguments.__IE_PrintType)
     {
         case "Prompt":
              if (printer.showPrintDialog()) 
                   PrintPrep();
              break;
         case "NoPrompt":
              PrintPrep();
              break;
         case "Preview":
         default:
              break;
     }
}

function PrintPrep()
{
     if (layoutrect1.contentDocument.readyState == "complete")
     {
         //这一块会被调用当提示用户后再打印的时候,因为打印对话框会给出时间,
         //让文档内容被完全装入。
         PrintNow();
     }
     else
     {
         // This block will usually be called when printing w/o user prompt
         // and sets an event handler that listens for the loading of the content
         // document before printing. Sometimes, however, the content document
         // will be loaded in time for the previous block to execute      
         layoutrect1.contentDocument.onreadystatechange = PrintWhenContentDocComplete;
     }
}

function PrintWhenContentDocComplete()
{
     if (layoutrect1.contentDocument.readyState == "complete")
     {
         layoutrect1.contentDocument.onreadystatechange = null;
         PrintNow();
     }
}

function PrintNow()
{
     printer.startDoc("Printing from Tmplt2.htm");
     printer.printPage(page1);   
     printer.printPage(page2);
     printer.stopDoc();
}
</SCRIPT>

<IE:TEMPLATEPRINTER ID="printer"/>
</HEAD>

<
BODY>
<IE:DEVICERECT ID="page1" CLASS="pagestyle" MEDIA="print">
     <IE:LAYOUTRECT ID="layoutrect1" CONTENTSRC="document" CLASS="lorstyle" NEXTRECT="layoutrect2"/>
</IE:DEVICERECT>
<IE:DEVICERECT ID="page2" CLASS="pagestyle" MEDIA="print">
     <IE:LAYOUTRECT ID="layoutrect2" CLASS="lorstyle" ONLAYOUTCOMPLETE="setTimeout('CheckPrint()', 100)"/>
</IE:DEVICERECT>
</BODY>
</HTML>

启动我们在《也谈WEB打印(二):简单的分析一下IE的打印原理并实现简单的打印和预览》中的程序,在Template Address中输入文档模板的路径,然后在 Document Address中输入www.cnblgos.com,然后单击Print,你看到了什么?我这里的效果如下:

    终于出现我们想要的打印框,按下打印,也还真的打印出来了。呵呵,心里非常高兴。那么奥妙在哪里呢?其实大家看了模板中的注释,已经比较清楚了,我再解释一下。
   我们先来看CheckPrint函数,其代码如下:
function CheckPrint()
{
     switch (dialogArguments.__IE_PrintType)
     {
         case "Prompt":
              if (printer.showPrintDialog()) 
                   PrintPrep();
              break;
         case "NoPrompt":
              PrintPrep();
              break;
         case "Preview":
         default:
              break;
     }
}

大家可能都注意到了,该函数就是检查一个叫做dialogArguments对象的__IE_PrintType属性,然后调用相应的函数或者什么都不做,当__IE_PrintType的值为"Prompt""NoPrompt"的时候,就调用PrintPrep函数,而该函数就是进行打印。所以,关键就在dialogArguments对象,弄清楚了他,一切都明白了。

IE下进行JS开发的人应该都知道,window对象也有一个属性叫做dialogArguments。他们会不会有什么关系呢?根据MSDN上说法,他们其实就是一回事,不过这个dialogArguments有一些局限,只能应用于打印模板。dialogArguments 不管是否向用户显示预览,他都是可用的,其中最重要的一个属性就是__IE_PrintType,他说明了打印模板是否应该向用户进行提示。

除了dialogArguments对象外,我们还接触到了一个很重要的事件,那就是ONLAYOUTCOMPLETE,他在打印或者预览版面在用源文档的内容完成了填充当前版面(layout)的时候触发,该事件还有一个至关重要的属性,那就是contentOverflow,如果为true,那么就表示文档还没有显示完,否则就是文档显示玩了,所以,我们只要在处理ONLAYOUTCOMPLETE事件的时候,根据contentOverflow的值进行判断,就知道需要多少个页面。

上面的例子尽管可以打印了,但还是不能根据文档内容生成适当的页面,下面的例子,我们就来一个可以根据文档内容动态获取页面数量的模板。

<!-- Template3.htm: 动态生成LayoutRect (仅可预览)
   本模板演示了如何在源文档动态的装入模板的时候动态的生成LAYOUTRECT元素。第一个LAYOUTRECT在BODY元素的onload事件处理程序AddFirstPage中生成,其余的LAYOUTRECT在每个LAYOUTRECT的onlayoutcomplete事件处理函数——OnRectComplete——中生成。OnRectComplete检查event.contentOverflow属性,如果contentOverflow为true,函数为一个新的DEVICERECT和LAYOUTRECT元素生成HTML。一旦HTML被生成,OnRectComplete就会使用insertAjacentHTML方法把新的HTML插入到pageContainer这个DIV中。在新的LAYOUTRECT被插入后,模板可以继续装载模板的源文档。注意OnRectComplete也把当前LAYOUTRECT的onlayoutcomplete事件处理句柄设为null.这样就阻止了文档在重新装入的时候——例如窗口的大小被更改的时候——调用OnRectComplete。
   本模板仅支持打印预览。
-->
<HTML XMLNS:IE>
<HEAD>
<?IMPORT NAMESPACE="IE" IMPLEMENTATION="#default">
<STYLE TYPE="text/css">
.lorstyle
{
    width:5.5in;
    height:8in;
    margin:1in;
    background:white;   
    border:1 dashed gray;
}
.pagestyle

    background:#FFFF99;
    border-left:1 solid black;
    border-top:1 solid black;
    border-right:4 solid black;
    border-bottom:4 solid black;
    width:8.5in;
    height:11in;
    margin:10px;
    overflow:hidden;
}
</STYLE>

<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()' EXTRECT='layoutrect2' CLASS='lorstyle'/>";
    newHTML += "</IE:DEVICERECT>";
    pagecontainer.insertAdjacentHTML("afterBegin", newHTML);
    iNextPageToCreate = 2;
}

function OnRectComplete()
{
    if (event.contentOverflow == true)
    {
        document.all("layoutrect" + (iNextPageToCreate - 1)).onlayoutcomplete = null;
        newHTML = "<IE:DEVICERECT ID='page" + iNextPageToCreate + "' MEDIA='print' CLASS='pagestyle'>";
        newHTML += "<IE:LAYOUTRECT ID='layoutrect" + iNextPageToCreate + "' ONLAYOUTCOMPLETE='OnRectComplete()' EXTRECT='layoutrect" + (iNextPageToCreate + 1) + "' CLASS='lorstyle'/>";
        newHTML += "</IE:DEVICERECT>";
        pagecontainer.insertAdjacentHTML("beforeEnd", newHTML);
        iNextPageToCreate++;
    }
}

</SCRIPT>

</HEAD>

<BODY ONLOAD="AddFirstPage()">
    <DIV ID="pagecontainer">
       <!--在这里动态的生成页面。 -->
    </DIV>
</BODY>
</HTML>

Document Address里输入保存有上述代码的htm文档的名字,然后单击Preview,效果如下图:

 
    博客园的首页在打印模板中的页数是
16页(这个数子当然不正确)。
    我们的打印模板现在可以根据被打印内容动态的生成页面了,可是还是有许多的内容没有完成,比如设置正确的纸张大小,设置页边距,设置页眉和页脚等。在下一篇文章,我们继续讨论这些内容。
    另外,老是用博客园的首页作为例子,不知DUDU有没有意见,如果有的话,就跟我说一声啊(^_^)。
    欢迎大家拍砖。

posted on 2007-10-15 09:00  永红  阅读(7249)  评论(7编辑  收藏