难道是我的分数不够多?
难道我的话题不够吸引人?

解决方案 »

  1.   

    近些年来,随着互联网的普及和推广,传统的单机模式和局域中的C/S模式的应用程序越来越不能满足信息共享的要求。因此,一种新的基于浏览器的B/S的应用程序的开发方式被提了出来。新的开发方案以其客户端的免维护、免配置、程序能根据服务器的信息能够自动更新升级;服务器端多层模式的应有提高处理的效率和安全性越来越被广大的应用程序的开发者所看好。成为应用程序开发的一个新的发展方向。在WINDOWS的平台上,人们利用ASP来开发服务的显示界面,而用组件来封装商业规则,在各种杂志上利用各种工具进行组件开发也是屡见不鲜。但介绍打印组开发却不怎么看到。笔者希望就自已开发打印组件的一点心得体会来抛砖引玉。二、设想  WEB打印组件要求是在一次为某单位开发信息管理系统中被提及的,这个系统中的一些票据需要打印。这样,如何对这些票据进行套打成了我们必须解决的问题。
      要在客户端打印一些用户要求的票据或其它的文字或图片资料,有两种方式:一种是利用DELPHI的ACTIVE FORM来生成客户端的界面,由浏览器下载安装,并在客户端运行。(这种方式在开发多后台数据库中,较为常用。在国内许多新版本的财务软件都采用了这种方法,其缺点是对开发人员的要求太高),另一种是开发一个组件在客户端安装,然后,由服务器端来生成VBSCRIPT脚本,在客户端运行、创建相应的组件对象;利用组件对象来进行打印操作。(这种方法只要客户端的COM组件开发完成,使用者只要熟悉VBSCRIPT或JAVASCRIPT脚本语言就可能方便的调用)。本文我们主要讨论第二种方式。
    三、实现
      首先、运行DELPHI 5.0;选择FILE菜单中的NEW,在弹出的对话框中选择ActiveX页,选择ActiveX Library创建ActiveX库.然后,再往新建的库中加入一个Automation Object,操作步骤同上。在Automation Object Wizard对话框中填入组件名prtTest3;单击OK。
      然后、在弹出的PrtTest3.tlb窗口中,选择IPrtTest3。单击右键新增一个IsInit的Property,类型为long。再继续增加如下5个Method:
    procedure prtCustomPage(PageWidth: Integer; PageHeight: Integer);safecall;
      procedure prtStart; safecall;
      procedure prtNewPage; safecall;
      procedure prtEnd; safecall;
      procedure prtDrawLine(X1: Integer; Y1: Integer; X2: Integer; Y2: Integer; LineWidth: Integer); safecall;
      procedure prtSetFont(const FontName: WideString; FontSize: Integer); safecall;
      procedure prtTextRect(VAlign: Integer; HAlign: Integer; RectLeft: Integer; RectTop: Integer;RectRight: Integer; RectBottom: 
           Integer; const PrintString: WideString); safecall;  它们意义是:prtCustomPage   用来设置自定义纸张.传入的参数自定义纸张的宽度和高度。
            prtStart    用来初始化打印机。如果初始化成功,则将属性IsInit置为真,反之则为假。 
            prtNewPage    用来使打印机换页。
            prtEnd     结束打印任务。
            prtDrawLine    用来在页面上绘制一条线。(X1,Y1)为起始点.(X2,Y2)为终止点。LineWidth为打印线的宽度。
            prtSetFont    用来设置打印的字体名及大小。
            prtTextRect    用来指定的方框内的输出的指定的字符串。
    四、调用
    在脚本中调用这个组件的方法:
    (1)、利用CreateObject函数创建一个打印对象。
    (2)、调用prtCustomPage来指定定制的纸张的大小(以0.1毫米为基本单位)。如果,不是定制的纸张则不需要调用这个过程(即以打印默认的纸张大小进行打印)。
    (3)、调用prtStart进行打印机的初始化工作。如果打印机初始化成功,则会将IsInit属性置为真,表示初始化成功;否则,表示打印机正在忙或有别的应用程序正在使用打印机,初始化不能成功。
    (4)、判断IsInit标志。如果为真,则继续执行打印段。
    (5)、执行打印段。可以在利用打印机画线、和在指定位置输出文字。
    (注:在组件接中的所有位置单位都是0.1毫米。例如.prtObject.prtDrawLien 0,0,1000,1000 表示从左上角(0,0)毫米到右下角(100,100)毫米处,画一条直线)
    (6)、利用prtEnd结束打印任务。
    <SCRIPT LANGUAGE="VBScript">
    <!--
    Sub TestPrtObject
       Dim prtObject
       Set prtObject=CreateObject("PrtTest3.PrtTest3")
       prtObject.prtCustomPage 1000,1000
       prtObject.prtStart
       if prtObject.IsInit then
         prtObject.prtDrawLine 0,0,1000,1000,1
         prtObject.prtDrawLine 500,700,1000,1000,1
         prtObject.prtSetFont "宋体",16
         prtObject.prtTextRect 2,2,0,0,1000,500,"WEB应用程序打印测试"
         prtObject.prtEnd
       end if
       Set prtObject=Nothing
    End Sub
      TestPrtObject()
    //-->
    </SCRIPT>
    五、代码解析
      在这个组件中,我们所要解决的几个问题:
    (1)、在Delphi的应用程序设计中自定义打印纸张的设置,Delphi中自身带了一个QuickReport的打印设计程序,这个程序在一定的程度上方便了打印的设计,但这个设计程序对于自定义纸张的设定和打印支持却不是很好。因此,在这个组件中我们采用手工代码来设定自定义纸张大小。
    Function TPrtTest3.InitPrintPaper:Boolean;
    Var
       Device:Array [0..cchDeviceName-1] of Char;
       Driver:Array [0..(max_path-1)] of Char;
       Port:Array [0..32] of Char;
       hDMode:THandle;
       pDMode:PDevMode;
    Begin
       Result:=True;
       if PrtIsCustomPaper then
        Begin
           {设置打印机段}
           Printer.GetPrinter(Device,Driver,Port,hDMode);
           if hDMode<>0 then Begin
             Try
               pDMode:=GlobalLock(hDMode);
               if pDMode<>nil then Begin
             //设定打印的方向为纵向或横向
                 if PaperOrientation<>0 then 
             pDMode^.dmOrientation:=DMORIENT_LANDSCAPE
                 else pDMode^.dmOrientation:=DMORIENT_PORTRAIT;
           //设置拷贝份数为1份.
                 pDMode^.dmCopies:=1;                        
           //以毫米为单位的纸张大小.
                 pDMode^.dmPaperLength:= PaperHeight*10;     
                 pDMode^.dmPaperWidth:=PaperWidth*10;
           //设置纸张类型为用户自定义.
                 pDMode^.dmPaperSize:=DMPAPER_USER;          
               end;
               GlobalUnLock(hDMode);
               Printer.SetPrinter(Device,Driver,Port,hDMode);
             except
               Result:=False;
             end;
           end else Begin
             Result:=False;
           end;
        end;
    end;
    (2)、打印位置的确定:由于组件是以图形方式进行打印,这就使得我们可以精确指向打印文件的输出位置,以便于进行对某些票据的套打。但这样就产生一个问题,我们是以打印的像素位置指定输入位置,还是以打印尺寸来指定位置。显然,利用像素来输出打印位置,对于编制程序来说是比较方便的。但由于一般用户对像素的概念并不理解,因此会带来使用上的困难;而且每一种打印机的分辩率是不一样的,因此,会出现在不同打印机上打印的效果不一样的问题。而以打印尺寸来指定位置并在程序中妥善加以处理就不会有上面的问题。具体的处理方法是,由用户输入尺寸位置。然后,在打印时,首先取得当前打印机的分辩率(注意:分辨率是以每英寸多少像素为单位的),再计算得出实际输入的像素位置后进行打印,这样就可以使得组件更加实用,而且,保持打印幅面的一致。具体的函数如下。
    Function TPrtTest3.MMtoPrintPixel(InputPoint:TPoint):TPoint;
    Begin
       Result.x:=MMtoPrintPixelX(InputPoint.x);
       Result.y:=MMtoPrintPixelY(InputPoint.y);
    end;Function TPrtTest3.MMtoPrintPixelX(InputX:Integer):Integer;
    Begin
       if QueryPrintlogPixel then
        Begin
           pDDIx:=GetDeviceCaps(Printer.Handle,logPixelsX);
           pDDIy:=GetDeviceCaps(Printer.Handle,logPixelsY);
           QueryPrintlogPixel:=False;
        end;
       Result:=Trunc(InputX/253.8*pDDIx+0.5);
    end;Function TPrtTest3.MMtoPrintPixelY(InputY:Integer):Integer;
    Begin
       if QueryPrintlogPixel then
        Begin
           pDDIx:=GetDeviceCaps(Printer.Handle,logPixelsX);
           pDDIy:=GetDeviceCaps(Printer.Handle,logPixelsY);
           QueryPrintlogPixel:=False;
        end;
       Result:=Trunc(InputY/253.8*pDDIy+0.5);
    end;GetDeviceCaps:为Windows系统中取得指定设备信息的函数。函数的原型如下
    int GetDeviceCaps(
        HDC hdc,    // 设置的句柄
        int nIndex     // 查询的设备参数。  
       );其中,HDC为想要查询的打印机的句柄,logPixelsX、logPixelsY表示要查询的内容是打印机横向、纵向的每英寸的像素点数。六、组件的注册
      如果是在Delphi编写了这个组件的,则可以选择Run菜单下的Register ActiveX Server来进行这个组件的注册。
      对于不开发机器的注册则可使用:点击“开始”→“运行”;在运行对话框中输入以下命令: 
      regsvr32  < 文件名 >        注册一个ActiveX控件
      regsvr32  /u  < 文件名 >    解除某ActiveX控件的注册七、其它应用
      组件的扩展:以上列出的仅仅是编写这个组件的框架,功能比较有限。根据需要我们还可以添加上画圆、椭圆,矩形,甚至图片等功能。有兴趣的读者就请自行添加。
    因为,组件是采用COM的方式运行,所以,我们还在Window Script Hosts(WSH)中应用这个组件,结合WSH原有(通过ADO对象)访问外部数据库的能