近些年來,隨著互聯網的普及和推廣,傳統的單機模式和局域中的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對象)訪問外部數據庫的能力,就可能做出在Windows下功能強大的WSH的腳本程序。