随笔 - 25,  文章 - 0,  评论 - 396
     在上一篇文章《也谈WEB打印(四):让我们的模板支持打印,并根据内容动态的生成页面中,我们知道了如何获取源文档的页数,并且知道了如何让我们的模板支持打印,在这篇文章中,我们讨论如何让打印模板获取正确的页面设置参数,并如何在打印模板中设置我们自己的UI首先,我们给打印模板加上页眉和页脚:
给打印模板加上页眉和页脚

   上面的模板演示了HEADERFOOTER元素的用处,并演示了如何给一个打印模板的页面添加页眉和页脚。他在BODY中增加了一个TEMPLATEPRINTER和一个HEADERFOOTER元素,每个页面有3个函数用来增加页眉和页脚,2个样式表来说明页眉和页脚的位置。在本模板中,增加页眉和页脚的工作出现在2个地方:第一处是OnRectComplete函数,一旦OnRectComplete生成了所有的LAYOUTRECT元素(那就是,当event.contentOverflow=false的时候),他就用页眉的格式化字符,文档的标题和URL初始化HeaderFooter行为,调用另外一个函数来计算页面的数量。OnRectComplete然后调用AddHeaderAndFooterToPage遍历所有的DEVICERECT以完成向每一个DEVICERECT添加页眉和页脚的实际工作,注意逻辑型变量bHeadersFootersAdded,尽管页眉和页脚按绝对位置放置,并且不会更改DeviceRect中的其他元素,当一个打印模板中DEVICERECT更改的时候,当前对LayoutRect行为的实现会重新组织LayoutRect,在这个重新初始化的时间内,每个LayoutRectonlayoutcomplete事件会重复的触发,导致一个无限调用OnRectComplete的无限循环。变量bHeadersFootersAdded在第一次传递给页面的后就变为true,以阻止重新组织页面的时候增加页眉和页脚,处理这个问题的另一个方法就是在初始化LayoutRect的时候就添加页眉和页脚。页面总数在layout完成的时候添加。把上面的模板保存,然后运行我们在《也谈WEB打印(二):简单的分析一下IE的打印原理并实现简单的打印和预览》中的例子,单击Preview,其效果如下:图中的“博客园-www.cnblogs.com”就是页眉。


  HEADERFOOTER元素专门处理模板中的页眉和页脚。而通过TEMPLATEPRINTER则可以访问大部分的与页面设置有关的配置信息:如页边距,纸张走向等。上面的例子,仍然没有取得正确的页面配置信息,在下面的例子中,我们就来设置正确的页面大小和页边距:
取得正确的页面设置


效果如下图:现在已经是正确的设置了。

  
    本模板集成了前面所有的模板,并增强了打印支持。在以前的例子中,为了简单起见,我们假设了纸张的大小,页边距,非打印区域,被打印的页面等等。本模板就读取那些存储在“页面设置”和打印对话框中大部分实际的值来格式化打印和预 览的页面,并仅仅打印用户希望的页面。一定要看看Init函数,样式表在这里被设置。因为打印机的计算单位是1/100英寸,所以,我们必须在使用前把这些值转换为英寸:除以100并在后面加上字符串“in”。  

最后,我们开始最cool的技术,在打印预览街面上放置我们自己的UI控制界面:

定义自己的UI



效果如下图:

 
    本例演示了如何生成你自己的打印预览用户接口。在本例中的用户接口允许用户访问页面设置和打印对话框,并且可以让用户缩放文档。为了建立用户接口,把BODY元素的SCROLL属性设置成了“no”,这样可以防止出现滚动条。“ui”、“div”是一个固定的,静态的区域,里面包含用户接口的按钮和文本框。ID为pagecontainer的DIV元素定义了内容页面显示的区域。他的样式表把他的overflow属性设置成了“auto”,这样的话,滚动条就会出现在他的上面(在需要的时候)而不是 BODY元素的上面。这样,用户就可以滚动到任何用户想看的页面而不用移动位于屏幕上方的用户接口按钮。一个带有用户接口的打印模板必须响应页面设置和打印设置的更改。首先要考虑的是当用户把页面尺寸变大的时候(例如把letter变成了legal纸)如何处理不在需要的LAYOUTRECT和DEVICERECT,并减少文档的页数。在当前打印模板的实现中,一旦LAYOUTRECT被生成了,一个模板就不要销毁他们,隐藏这些不需要的LAYOUTRECT是一个更好的方法。你有许多的方法来处理这个问题,一个有效的方法就是用绝对定位在屏幕隐上藏这些元素而不不要使用display属性。这些没有显示的LAYOUTRECT不会调用onlayoutcomplete事件。

在本模板中,class为.pagestyle的DEVICERECT元素具有一个left属性,他的值是-50英寸,该属性会被忽略,如果position属性没有设置或者设置为“static”。因此当一个DEVICERECT元素的positioin属性设置为“absolute”的时候,该页就会从屏幕上消失(因为他的left=-50in)。模板添加了一个函数——ShowFilledPagesAndHideExcessPages——它根据headfoot.pageTotal的值设置所有DEVICERECT的position属性为“static”或“absolute”,不管什么时候,如果conentOverflow的值在onlayoutcomplete事件处理函数中为false,那么headfoot.pageTotal值就会被设置,ShowFilledPagesAndHideExcessPages也会被调用。OnRectComplete在本质上仍然与前面所示模板的功能一样,是onlayoutcomplete事件的处理函数,但有所不同,它仅是最后一个LAYOUTRECT的onlayoutcomplete事件的处理函数,对于其他的LAYOUTRECT,只有在第一次生成的时候才是,一旦这些LAYOUTRECT生成了,他们的onlayoutcomplete事件处理函数就在AddNewPage函数被切换成OnRectCompleteSimple。当contentOverflow为true的时候,OnRectCompleteSimple不会增加新的页面。

DoPageSetup函数是Page Setup 按钮的onmouseup事件的处理。当该按钮被按下的时候,页面设置对话框就会出现,.pagestyle,.lorstyle,.headerstyle和.footerstyle这4个class都会根据变化重新初始化。注意页面的排版会在这些class做实际改变的时候重新组织(这会导致onlayoutcomplete事件)。

为了使得用户在打印预览的时候可以缩放页面的大小,本模板增加了一个函数——Zoomer。Zoomer是Zoom In 和Zomm Out按钮的onmouseup事件和文本框的onkeyup事件处理程序。当用户单击任何一个按钮或者更改zoom文本框的值的时候,它更改ID为zoomcontainer的DIV元素的zoom属性(初始值是50%)。

DoPrintFromPreview函数是Print按钮的的onmouseup处理句柄,他只是简单的显示打印对话框,并调用与前一个例子中一样的PrintPrep函数。

现在看来,似乎我们可以完全控制打印了,其实不然,比如我们现在就不可以控制纸张的大小,根据MSDN的说法,可以根据__IE_PrinterCmd_DevMode属性获取打印机的设置,该属性的值是一个DEVMODE结构,通过更改该结构的值就可以控制纸张了。而实际上,无论在什么时候,什么地方,在我们的模板中,该属性的值都是null,其他的属性比如__IE_PrinterCmd_DevNames也是null,看来,MS已经把这些与硬件相关的东西屏蔽掉了。要想真正的做到“完全”控制,可能需要写一个HOOK了,但是这个已经超出了本文讨论的范围,而且,我本人对与VC也不是很熟练,也就不在这献丑了。

这又长又臭的Web打印文章终于完成了,感谢各位的光临,让高手见笑了。

另有朋友问我如何不显示打印对话框而直接打印,那就是把IOleCommandTarget的Exec的第3个参数设置为OLECMDEXECOPT_DONTPROMPTUSER建议大家在没事的时候,多多看看MSDN(^_^)。

至于用ActiveX进行打印,我在这里简单的讲一下原理,用VC开发最简单,建立一个ATL项目,定义好方法和属性,把资源模板打包进项目,然后把模板的路径变为“res://xxx.dll/模板的名字.htm”就可以了。当然也可以用Delphi等实现。我没有用过VB,应该用VB也是可以实现的吧。至于如何把ActiveX控件的值传入模板,我采用的方法就是现在模板中定义好参数的格式化字符串,把在dll中的模板资源读入内存,替换掉相应参数的格式化字符串,然后把新的内容存入一个临时文件,然后把模板的路径指向该临时文件。

注:本系列文章中的模板代码,都出自MSDN,我仅对某些模板的注释进行了一些翻译或者对模板进行了一些解释。版权不归我,请在用上述代码进行商业用途而被人起诉的时候不要找我(^_^)。

posted on 2007-10-16 10:59  永红  阅读(8630)  评论(21编辑  收藏