有關在java中實現文檔打印的典型說法描述了一個復雜的過程,它要求對字體進行測定、對文本進行解析并將結果繪制到一個Graphics對象中。這個過程似乎執行起來相當困難,并且它和用于文檔視頻顯示的高級編程方法不一致。假如你想要花費大量的精力來完成這個過程,那么你就不會首先想到要在Java中編程。
你一定不想自己完成測定、解析和繪制過程,而是希望通過簡單地將文檔發送到一個能為你處理所有事情的對象中來完成這個任務。本文就將介紹這樣一個對象――DocumentRenderer,它將一個文檔作為方法中的一個參數并處理指定任務來完成打印。比如,用這個類來顯示一個Html文檔需要兩個步驟:構造一個DocumentRenderer類的實例并將HTML文檔作為參數發送到PRint(HTMLDocument)方法中。DocumentRenderer類用于處理打印該文檔所必需的開銷,包括顯示一個打印對話框以及格式化文本。下載用于該類的源代碼(DocumentRenderer.java)。
我們設計了這個DocumentRenderer類以便利用這個已經在Java中可用的高級文本性能。 根據可重用和可擴展類的原則,我們使用了一些現有的對象(Java Swing Text Package用它來對顯示結果進行格式化)使打印結果能夠顯示在紙上。用這種方法設計DocumentRenderer使我們能夠用比前面討論過的方法少寫近200行的代碼來建立這個類。
除了能少寫代碼之外,使用DocumentRenderer 實現中的現有對象還提供了一些額外的功能使得該類更加通用。在最初設計這個類的時候,我們只是打算將HTML文檔打印出來。而添加一些功能來打印其他類型的文檔則是后來的想法。當我們發現只需在用于HTML 打印類的代碼中加上約6行的額外代碼就可以打印一個Rich Text Format文檔時,便在這個項目的后期添加了這個功能。
顯示文檔
DocumentRenderer能夠用來打印幾類包含在JEditorPane中的文檔。我們用三種類型的文檔(JEditorPane能夠缺省識別的)測試了這個printer類:HTMLDocuments、PlainDocuments和Rich Text Format文檔。只需要做一些很小的改動,這個類就應該可以將包含在JEditorPane中的其他類型文檔打印出來了。
DocumentRenderer類將一個文檔的打印形式從其視頻顯示中區分出來了。這就使你能夠針對特定的打印結果進行文本格式化,而會不影響其屏幕顯示。DocumentRenderer采用所有打印頁面的實際大小來顯示文本并計算出行間距(line break)。當文檔的寬度不足以顯示在打印頁面上時,該類會答應使用縮放(scaling)。
DocumentRenderer相當智能。分頁符(page breaks)不會將一行單一語句分放到兩個頁面上。字符也不會被切成兩半,不會象瀏覽器在處理一般打印時會在這一頁末尾顯示一句話的上半部分而在下一頁的頂部顯示這句話的下半部分。這個類能夠處理大量字體、顏色和小圖標。分欄文本(columnar text)的顯示也沒有問題。對于每一個JEditorPane能夠顯示的文本特性,通常DocumentRenderer都可以將這種性能呈現到紙上。
你只需使用兩行代碼便可以將DocumentRenderer結合到你的程序中去了。用一個不帶參數的構造器創建這個類的實例,再調用一個合適的打印方法來處理其他事情。比如,以下代碼將打印這個htmlDocument,它是HTMLDocument類的一個有效實例:DocumentRenderer DocumentRenderer = new
DocumentRenderer();
DocumentRenderer.print(htmlDocument);
它給用戶顯示一個打印對話框,答應用戶選擇打印機、打印數量等,同時還有一個取消打印的選項。
PlainDocuments使用了print(PlainDocument)方法,采用和HTMLDocuments相同的方式執行打印。由于在Java中不能直接訪問Rich Text Format文檔,所以你必須將這種類型的文檔發送到DocumentRenderer中(通過將它封裝到一個JEditorPane里),就象這樣:DocumentRenderer.print(jeditorPane);
這里的jeditorPane是JEditorPane的一個有效實例,其中包含一個Rich Text Format文檔。
為了給用戶提供方便,你可以調用DocumentRenderer的pageDialog方法來顯示一個打印對話框以便用戶可以調整頁面大小、頁邊設置和紙的打印方向(orientation)。DocumentRenderer還提供了一種方法使開發人員可以選擇是否對那些無法在打印寬度內完全顯示的文檔進行按比例縮放。我們認為能夠進行縮放通常是比較好的選擇,因為它能夠防止文本在正確的頁邊處被分開,但它似乎比較適合讓用戶去選擇。這種名為setScaleWidthToFit(boolean)的方法提供了按比例縮放的選項。你必須確定在調用打印方法之前調用這個scaling和pageDialog方法。
了解DocumentRenderer
DocumentRenderer用于執行顯示一個打印對話框并通過使用在Java Swing Printing API中可用的標準化工具開始打印操作。由于在使用這個DocumentRenderer類時無需完全了解這個API,而且該API已經在很多地方被具體描述過(見資源),因此這里我們就不再介紹它了。這個用于DocumentRenderer類的源代碼中還包括了這個打印邏輯的完整文檔。
然而我們或許應該解釋一下DocumentRenderer用來在單獨打印頁面中定位文本的過程,以便你了解這個類所提供的改進功能,這會幫你回顧這個Java打印過程通常遵循的顯示文檔的邏輯。
文檔通常是以一種簡單的方式進行打印的。首先,文檔會被放入JEditorPane。你可以將打印過程想象為在JEditorPane 的上面放置一個矩形框(其大小和頁面打印區的大小相等),并對其中的內容進行打印而無需關心外面的部分。
這個矩形的上沿與JEditorPane 頂部齊平,矩形框內部的區域會被繪制(paint)。假如矩形的下沿穿過文本,不用去管它;字符會在打印頁面的底部被分開。打印第二頁時,矩形的上沿被向下移至前面被下沿所占據的那一行,該過程被重復執行。由于第二頁正好在第一頁結束的地方開始,因此在第一頁底部被分開的字符會出現在第二頁的頂部。后面幾頁也是一樣。
為了避免從行中斷開,DocumentRenderer會仔細檢側文檔以測定是否一個單獨文本和頁面完整契合。這樣會比只是在JEditorPane中放一個矩形框并打印其中內容的效果更佳。
繪制視圖(View)
假如將JEditorPane看作僅包含了一個文檔的方法,你就不能測定所有文本的位置或大小了。一個文檔對于這個任務來說太大了,文檔或許會契合于一個單一頁面,或許不會。為了使文檔能夠完整契合于打印紙,你必須將它分成一些小的部分以便對每個部分的位置進行檢測。
幸運的是,Java Swing Text Package提供了一個View類,它能使你將文檔分成單一的、適于繪制的部分。你可以將JEditorPane想象成是由幾個視圖部分組成的;現在你就可以完全基于這些小部分的大小和位置來打印文檔了。
View類的子類用于處理可視性組件(visual component)中的顯示和打印文本的任務。然而,處理打印文本的許多程序員沒有意識到的是視圖可以在顯示到紙上時提供這些相同的功能。盡管對視圖問題進行具體討論是本文以外的話題,但在討論文檔打印時對它有一個大致的了解還是很有必要的。
在Swing中,視圖被當作處理文本顯示的容器。在樹型目錄中一個根視圖可以有多個視圖分支。在這些分支的端部會顯示代表真實文本的葉視圖(leaf view)。
將這個視圖的樹型結構當作一個由單一的、大的、包含整個文本的視圖來考慮。這個文檔視圖被分成幾個段落視圖,它被依次分成幾個單獨的行。盡管真實工作中的視圖情況要比這個簡單的描述復雜的多,但該例子中顯示了如何通過視圖來將一個文檔分成契合于打印紙的小的部分。通過查看每一行,你可以測定它是否完全契合于打印紙而不會在底部被分開。假如行數契合,就執行打印,假如不契合,則將它記錄下來以便在下一頁中執行打印。
包含在JEditorPane中的視圖采取了一種和在JPanel中的組件行為相類似的操作。一個主要區別在于視圖不要求布局治理器(layout managers)來進行位置處理;它們會自己參與布局。這樣一來,在JEditorPane中的視圖就會象一個真實的組件和布局治理器一樣進行操作。視圖知道如何查看、如何繪制自己以及在哪里顯示其子文檔。
視圖并不是被直接建立的。更確切地說,它們是由ViewFactory子類的工廠(factory)來生成的。一個ViewFactory生成一個文檔并將它們分成根視圖以及所需的分支視圖和葉視圖。工廠會按照這種方式來處理這些乏味的解析文檔和計算布局的工作。
你很少能夠直接和這些工廠打交道。對很多部分來說,它們是被自動調用的。在JEditorPane中設置文檔并調用JEditorPane.validate()方法來將文檔發送的適合的工廠中,該工廠則會返回所需的視圖。然后這些視圖會被用在組件的布局上。
打印視圖
DocumentRenderer類能夠將需要打印的文檔放入jeditorPane中,它是JEditorPane的一個實例。jeditorPane的寬度決定了打印頁面的大小而且它會調用一個驗證方法來執行布局。DocumentRenderer不會顯示這個JEditorPane,因此屏幕顯示不會生效。需要被打印的根視圖是通過一個有點復雜的jeditorPane用戶界面調用來獲得的:View rootView =
jeditorPane.getUI().getRootView(jeditorPane);
這個rootView及其子視圖可能會對所需信息進行查詢以便對打印文檔進行布局。這些視圖在每部分文本的繪制環境(graphical context)中提供了坐標和大小。通過這些信息你就能夠測定這部分文本是否和打印頁面相契合。假如契合,則DocumentRenderer將執行打?。患偃绮黄鹾?,該類將對這個用來打印這部分文本而不將其分開的分頁符進行測定。
由于視圖知道如何自行繪制,因此你無需自己設置字體或顏色。DocumentRenderer以多種字體和顏色通過調用一個簡單視圖的paint方法來處理式樣文本(styled text)。
然而這個視圖的樹型結構也存在一個問題。你不能確定在一個代表打印文本的葉視圖結束之前分支視圖從根視圖中被分出了幾次。你可以通過使用DocumentRenderer 類中的一個簡單的循環方法來解決這個問題。
printView方法循環經過視圖的分支結構來查找可打印的葉視圖。這種方法將一個視圖作為其中一個參數。它從根視圖開始對每個視圖進行檢查以測定它是否有相關的子視圖。假如有,則printView會依次調用每個子視圖將其作為其視圖參數。這樣一來,這個方法最終會運行至整個樹型結構。當該方法發現一個不帶子視圖的葉視圖時,它會在用于打印的繪制環境中檢測它的位置。這個方法只用于繪制那些完全契合于該環境的可打印部分的葉視圖。當一個葉視圖分跨(straddle)頁面的底部時,這個方法就會記錄該頁面上沿的位置以便使下一頁從這個位置開始。因此分頁符就不會從文本的行當中斷開了。
假如你想要更深入地了解打印視圖的用法,你可以查找DocumentRenderer類的源代碼。我們對它進行了注釋講解。由于它只有不到200行的代碼,因此我們有充足的時間來給它加上注釋。
該類的局限
我們在Java SDK 1.3和1.4版本中對DocumentRenderer進行過測試,盡管它應該是適用于實現Swing的所有Java版本的。DocumentRenderer是通過標準的Java技術來運行的,因此它不能實現比Java本身更好或更糟的文本繪制。在JEditorPane中不能顯示的字符也將無法顯示在紙上。
Java在Windows環境下對文本進行測定時有一個小問題。由于文本沒有被精確地測定,一些位置可能有些偏離。由于這些錯誤很小所以通常不是什么大問題,但它會在對文本右對齊(right-justified )時變得很麻煩。因此,要盡量避免使用右對齊方式。DocumentRenderer類不會產生這個問題,它似乎也不會出現在linux環境中。
最后要說的是,大圖標是不能被打印出來的。在Java中不能將它們顯示在頁面中,但小圖標是沒有問題的。
在Java中執行打印不再會是一個復雜的問題了。只需簡單地寫兩行代碼并通過DocumentRenderer類來將高級的文本打印功能添加到你的程序中可以了。
新聞熱點
疑難解答