随心所欲的Web页面打印技术

系统 1531 0

随心所欲的Web页面打印技术

一.概述
  对基于B/S架构的应用程序而言,客户端的页面打印一直是比较头疼的问题,简单的做法是:1.使用IE的打印功能;2.使用水晶报表。但以上两种办法,都有很大的局限性,很难实现特殊要求的排版和精确的定位,所以不能满足一些特殊客户的BT要求。为此,本人总结了自己在使用Web打印上的一点经验,和大家分享。
  本文涉及以下技术:javascript、ActiveX、ASP.NET、GDI+。

二.基本架构
  首先,我们不能使用IE的打印功能,必须自己设计‘打印’按钮。很多人习惯将‘打印’按钮放在要打印的页面上,打印时为了不把这个按钮打印出来,采用办法如下:1.打印前隐藏按钮;2.打印;3.显示按钮。
我觉得这样比较麻烦,所以我采用框架。一共有三个页面:
1.main.htm :框架页面,上面是打印按钮,下面是要显示的页面。
2.header.htm :标题栏,至少包含一个打印按钮。
3.report.aspx :要打印的页面,由用户生成。

//main.htm
<html>
 <head>
  <title></title>  
 </head> 
  <frameset rows="10%,90%" frameborder="0" border="0" frameSpacing="0">
  <frame id="header" name="header" src="Header.htm" noresize scrolling="no">
  <frame id="report" name="report" src="Report.aspx" noresize scrolling="auto">
 </frameset> 
</html>

//header.htm
<html>
 <head>  
  <script id=clientEventHandlersJS language=javascript>
  <!--
  function btnPrint_onclick()
  {   
     parent.report.focus();
     parent.report.print();  
  }
  //-->
  </script>
 </head>
 <body> 
  <INPUT id="btnPrint" type="button" value="Print" name="Print" onclick="return btnPrint_onclick()">  
 </body>
</html>

  这样,在点击‘打印’按钮时,将直接打印report.aspx页面的内容,既简单又直观。

三.打印
 要完全控制打印,就必须由程序设定页眉、页脚、页边距。每个客户端的IE设置都不尽相同,你可以要求你的客户修改他们的打印设置为你指定的值,显然这不现实。所以正确的做法是:
1.备份客户打印页面设置;
2.设置页眉页脚上下左右边距为自己需要的值;
3.打印;
4.恢复原来的打印页面设置。

 这里用到一个叫ScriptX的控件。你需要下载一个文件:smsx.cab,下载地址: http://www.meadroid.com/scriptx/sxdownload.asp 。这个地址并不能保证长期有效,你可以在搜索引擎上搜索‘ScriptX’以获得更多相关信息。

在“header.htm”中增加该控件的引用:
<OBJECT id="factory" style="DISPLAY: none" codeBase=" http://localhost/WebApplication1/smsx.cab#VVersion=6,3,434,12 "
 classid="clsid:1663ed61-23eb-11d2-b92f-008048fdd814" viewastext>
</OBJECT>
注意CodeBase指向你的实际存放文件的位置,在客户端第一次浏览该页面时,将下载并安装该控件,请确定客户端的安全设置允许安装控件。
那么此时 header.htm 的内容如下。

//header.htm
<html>
<head>
 <script id="clientEventHandlersJS" language="javascript">
 <!--
 function btnPrint_onclick()
 {
  //备份客户打印机设置
   var h = factory.printing.header;
   var f = factory.printing.footer;
   var t = factory.printing.topMargin;
   var b = factory.printing.bottomMargin;
   var l = factory.printing.leftMargin;
   var r = factory.printing.rightMargin;
 
   //设置页眉页脚上下左右边距 
   factory.printing.header = "";
    factory.printing.footer = "";
    factory.printing.topMargin="0";
   factory.printing.bottomMargin="0";
   factory.printing.leftMargin="0";
   factory.printing.rightMargin="0";
 
   //打印
   parent.report.focus();
   parent.report.print()
 
   //恢复原来的打印设置
   factory.printing.header = h;
   factory.printing.footer = f;
    factory.printing.topMargin=t;
   factory.printing.bottomMargin=b;
   factory.printing.leftMargin=l;
   factory.printing.rightMargin=r; 
 }
 //-->
 </script>
 
 <OBJECT id="factory" style="DISPLAY: none" codeBase=" http://localhost/WebApplication1/smsx.cab#VVersion=6,3,434,12 "
  classid="clsid:1663ed61-23eb-11d2-b92f-008048fdd814" viewastext>
 </OBJECT>
</head>
<body bgColor="#9999cc">
 <INPUT id="btnPrint" type="button" value="Print" name="Print" onclick="return btnPrint_onclick()">   
</body>
</html>

  此时,不管客户端IE设置如何,都能正确打印页面,打印内容将完全取决于页面:report.aspx。

四.动态页面生成
1. 如果你的页面是静态页面,或者页面元素为固定数量,那就非常简单了。只要调整好各个元素位置就行了,绝对所见即所得。要注意的是一个A4纸的大小为21cm×29.7cm,对应象素大约为 794×1123 (系统分辨率96DPI) 。

2.如果你要生成一些图表,可以使用GDI+,你自己绘制的图片绝对能满足客户需求。下面给一个例子,我在页面放一个Label,一个Image。Image动态生成,调整其位置使其打印时出现在第二页的左上角。

 1 public   class  Report : System.Web.UI.Page
 2   {
 3    protected  System.Web.UI.WebControls.Image Image1;
 4    protected  System.Web.UI.WebControls.Label Label1;
 5  
 6    Web 窗体设计器生成的代码 #region  Web 窗体设计器生成的代码
 7    override   protected   void  OnInit(EventArgs e)
 8    {   
 9    InitializeComponent();
10     base .OnInit(e);
11   }

12    private   void  InitializeComponent()
13    {    
14     this .Load  +=   new  System.EventHandler( this .Page_Load);
15   }

16    #endregion

17
18    private   void  Page_Load( object  sender, System.EventArgs e)
19    {   
20     if ( ! Page.IsPostBack)
21     {
22     InitImage();
23    }

24   }

25
26    private   void  InitImage()
27    {
28    Bitmap bmp  =   new  Bitmap( 800 , 1120 );
29    Graphics g  =   Graphics.FromImage(bmp);
30
31    g.FillRectangle(Brushes.Gray, 0 , 0 , 800 , 1120 );
32
33    g.FillRectangle(Brushes.RoyalBlue, 0 , 0 , 100 , 600 );
34    g.FillRectangle(Brushes.Aqua, 600 , 0 , 100 , 600 );
35    g.FillRectangle(Brushes.Coral, 700 , 0 , 100 , 600 ); 
36  
37    g.FillRectangle(Brushes.YellowGreen, 0 , 800 , 800 , 100 );
38    g.FillRectangle(Brushes.Beige, 0 , 900 , 800 , 100 );
39    g.FillRectangle(Brushes.SkyBlue, 0 , 1000 , 800 , 100 );
40    g.FillRectangle(Brushes.Tomato, 0 , 1100 , 800 , 20 );
41
42     string  filename  =  Server.MapPath( " TempImages\\img1.jpg " );   
43
44    bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Jpeg);
45
46     this .Label1.Text  =  filename;
47     this .Image1.ImageUrl  =  filename;
48     this .Image1.Attributes[ " style " ] = " POSITION: absolute; LEFT: 0cm;  TOP: 29.7cm " // 定位
49   }
  
50  }

51
52

注意:要保证存放图片的目录,有写权限。

3.以上技术只适合于页面元素为固定数量的情况,对于页面内容大小不定的情况,例如,要打印一份人员信息的清单,人员数量为1~1000不等,每页显示20条记录,要有规定的页眉、页脚,此时该如何处理。
  思路:ASP.NET页面都有一个基类System.Web.UI.Page,该类有一个保护方法叫void Render(HtmlTextWriter writer),就是通过这个方法,ASP.NET在后台把WEB服务器端控件的属性转换成HTML代码,并发送到客户端供浏览器显示。我们的办法就是“劫持”该方法,完全手工生成所需页面标记代码。
先看一个例子:
 public class FrmRYInfo : System.Web.UI.Page
 {
  private void Page_Load(object sender, System.EventArgs e)
  {   
  }
  protected override void Render(HtmlTextWriter writer)
  {
   writer.Write("<HTML>");
   writer.Write("<body>"); 
   writer.Write("<h1>Hello,world!</h1>");  
   writer.Write("</body>");
   writer.Write("</html>");
  }
 }
  编译并运行以上程序,可以看到一行一号字体的"Hello,world!",查看页面源文件,内容如下:
 <HTML><body><h1>Hello,world!</h1></body></html>
  对照上面代码,应该非常好理解,下面我们就做一个实际的例子,将信息从数据库读出,显示在页面上,每页显示15条记录。

  protected   override   void  Render(HtmlTextWriter writer)
  
{
   writer.Write(
" <HTML> " );
   writer.Write(
" <body> " );  

   DataTable tabRY 
=  GetCustomerInfo();  // 读取数据库

   
int  Lines  =   15 ;     // 每页行数
    int  Count  =  tabRY.Rows.Count;
   
int  TotalPage  =  Count / Lines  +  (Count % Lines == 0 ? 0 : 1 );

   
for ( int  CurrentPage  = 0 ; CurrentPage < TotalPage; CurrentPage ++ )
   
{
    
int  StartRow  =  CurrentPage  *  Lines;
    
int  EndRow  =  StartRow  +  Lines;
    
if (EndRow  >  Count) EndRow  =  Count;

    ProcessCurrentPage(writer,tabRY,StartRow,EndRow,CurrentPage,TotalPage);
   }

  
   writer.Write(
" </body> " );
   writer.Write(
" </html> " );
  }


  
private   void  ProcessCurrentPage(HtmlTextWriter writer, DataTable tabRY,  int  StartRow,  int  EndRow,  int  

CurrentPage, 
int  TotalPage)
  
{
   
if (CurrentPage  !=   0 )
   
{
    writer.Write(
" <p  style=page-break-before:always></p> " );
              }

   
   writer.Write(
" <table  width=630 height=417 border=0> " );
   writer.Write(
"  <tr> " );
   writer.Write(
"   <td width=624 height=47><div align=center  style=font-size:24px>人员信息汇总

</ div ></ td > " );
   writer.Write( "   </tr> " );
   writer.Write(
"   <tr> " );
   writer.Write(
"   <td height=222> " );

   writer.Write(
"    <table width=623  border=1 cellpadding=0 cellspacing=0> " );
   writer.Write(
"    <tr> " );
   writer.Write(
"     <td width=134><div align=center>姓名</div></td> " );
   writer.Write(
"     <td width=134><div align=center>编号</div></td> " );
   writer.Write(
"     <td width=134><div align=center>电话</div></td> " );
   writer.Write(
"     <td width=134><div align=center>小灵通</div></td> " );         
   writer.Write(
"    </tr> " );

  
for ( int  i = StartRow; i < EndRow; i ++ )
  

   DataRow row 
=  tabRY.Rows[i];
   
string  XM  =  row[ " MC " ].ToString();
   
string  BH  =  row[ " BH " ].ToString();
   
string  DH  =  row[ " LXDH " ].ToString(); if (DH.Length == 0 )DH = " - " ;
   
string  XLT  =   row[ " XLT " ].ToString();

   writer.Write(
"    <tr> " );
   writer.Write(
"     <td width=134><div align=center> "   +  XM  +   " </div></td> " );
   writer.Write(
"     <td width=134><div align=center> "   +  BH  +   " </div></td> " );
   writer.Write(
"     <td width=134><div align=center> "   +  DH  +   " </div></td> " );
   writer.Write(
"     <td width=134><div align=center> "   +  XLT  +   " </div></td> " );     

    
   writer.Write(
"    </tr> " );
  }


   writer.Write(
"    </table> " );

   writer.Write(
"   </td> " );
   writer.Write(
"   </tr> " );
   writer.Write(
"   <tr> " );
   writer.Write(
"   <td height=37><div align=right>第 "   +  (CurrentPage + 1 ).ToString()  + " 页,共 "   +  

TotalPage.ToString() 
+   " 页</div></td> " );
   writer.Write(
"   </tr> " );
   writer.Write(
" </table> " );
  }


感觉又回到了用记事本做网页的年代,手工生成HTML代码,是不是真正叫“随心所欲”。
几点说明:
(1)在每一页(除了第一页)的头部加入 writer.Write("<p  style=page-break-before:always></p>"); 目的是控制在打印时,打印机在此换页。这里通过强制打印机换页来实现页面的布局,与上面的绝对定位的办法不同。该标记只影响打印,不影响显示。
(2)用记事本做网页绝对很痛苦,而且HTML标记也很不好用,我的办法是:用Dreamweaver生成需要的页面,再参照其HTML代码进行编程。
(3)尽量使用HtmlTextWriter类提供的一些其它方法如WriteBeginTag等取代Write方法,这样可以提高页面在客户端的兼容性。同时在每个标记后加入writer.WriteLine();进行换行,以便于调试。

 

随心所欲的Web页面打印技术


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论