目次|前|次 |
アプリケーションでは、Java印刷APIを使って次の処理が可能です。
Java印刷APIは、コールバック印刷モデルに基づいています。このモデルでは、アプリケーションではなく印刷システムが、ページを印刷するときの制御を行います。アプリケーションは、印刷するドキュメントについての情報を提供し、印刷システムは、各ページのレンダリングが必要になると、それをアプリケーションに要求します。
印刷システムは、特定のページのレンダリングを2回以上要求したり、実際とは異なる順序でページのレンダリングを要求したりする場合があります。印刷システムからどのページが要求されても、アプリケーションは正しいページ・イメージを生成できなければなりません。この点、印刷システムはウィンドウ・ツールキットと似ています。ウィンドウ・ツールキットは、いつでも、どのような順序でも、コンポーネントに再描画を要求できます。
コールバック印刷モデルは、従来のアプリケーション駆動の印刷モデルよりも柔軟性があり、より広範なシステムとプリンタでの印刷に対応しています。たとえば、逆の順序で出力ページがスタックされるプリンタの場合、印刷システムはアプリケーションに逆の順序でページを生成するように要求することで最終のスタックを正しい読取り順序にすることができます。
このモデルを使用するアプリケーションは、十分なメモリーまたはディスク領域がないためページ全体のビットマップをバッファリングできないコンピュータからでも、ビットマップ・プリンタに印刷できます。この場合、ページは一連の小さなビットマップ、つまりバンドとして印刷されます。たとえば、1ページの10分の1をバッファリングできるだけのメモリーしか利用できない場合、ページは10個のバンドに分割されます。印刷システムは、アプリケーションに対し、ページごとにレンダリングを10回要求し、1回で1つのバンドを埋めます。アプリケーションは、バンドの数またはサイズを意識する必要はありません。要求されたときにそのページをレンダリングできれば十分です。
アプリケーションは、印刷機能をサポートするために、次の2つのタスクを行う必要があります。
ユーザーは通常、アプリケーションのボタンをクリックしたりメニュー項目を選択したりして、印刷を開始します。ユーザーが印刷操作を起動すると、アプリケーションは、PrinterJob
オブジェクトを作成し、それを使って印刷処理を管理します。
アプリケーションは、印刷ジョブを設定し、ユーザーに対して印刷ダイアログを表示し、印刷処理を開始します。
ドキュメントを印刷する場合、アプリケーションは印刷システムからの要求に対して各ページをレンダリングする必要があります。このメカニズムをサポートするため、アプリケーションは、Printable
インタフェースを実装したページ・ペインタを提供します。印刷システムでページのレンダリングが必要になると、印刷システムはページ・ペインタのprint
メソッドを呼び出します。
ページ・ペインタのprint
メソッドが呼び出されると、メソッドにはページ・イメージのレンダリングに使うGraphics
コンテキストが渡されます。また、ページの幾何学的レイアウトを指定するPageFormat
オブジェクトと、印刷ジョブ内でのページの順番を示す整数のページ・インデックスも渡されます。
印刷システムは、Graphics
とGraphics2D
の両方のレンダリングをサポートしています。Java 2DのShape
、Text
およびImage
を印刷するには、print
メソッドに渡すGraphics
オブジェクトをGraphics2D
にキャストします。
ページによって異なるページ・ペインタと書式を使用するドキュメントを印刷するには、ページング可能(Pageable)ジョブを使用します。Pageableジョブを作成するには、Book
クラスを使用するか、またはPageable
インタフェースの独自の実装を使用します。単純な印刷操作を実装する場合は、Pageable印刷ジョブを使用する必要はありません。すべてのページが同じページ書式とページ・ペインタを共有する場合はPrintable
を使用できます。
ページ・ペインタの主要なジョブは、印刷システムから提供されるグラフィックス・コンテキストを使って、ページをレンダリングすることです。ページ・ペインタは、次のPrintable
.print
メソッドを実装しています。
print
メソッドに渡されるグラフィック・コンテキストは、Graphics
またはGraphics2D
のインスタンスです。どちらを使用するかは、Java仮想マシンにロードされているパッケージによって決まります。Graphics2D
の機能を使用するには、Graphics
オブジェクトをGraphics2D
オブジェクトにキャストします。print
に渡されるGraphics
インスタンスは、PrinterGraphics
インタフェースも実装しています。
Printable
に渡されるPageFormat
では、印刷するページの幾何学的な配置(ジオメトリ)が記述されています。print
に渡されるグラフィックス・コンテキストの座標系は、そのページに固定されています。座標系の原点は、用紙の左上隅です。X座標の値は右に向かって、Y座標の値は下に向かってそれぞれ増加し、単位は1/72インチです。ページの向きが縦の場合、x軸は用紙の「幅」の方向になり、y軸は用紙の「高さ」の方向になります。用紙の高さは幅より長いのが普通ですが、そうでない場合もあります。ページの向きが横の場合は、軸と用紙の関係が逆になり、x軸が用紙の「高さ」方向、y軸が用紙の「幅」方向になります。
用紙の縁まで印刷できるプリンタは少ないので、PageFormat
ではページのイメージング可能領域が指定されています。これは、ページの中で安全にレンダリングできる部分です。イメージング可能領域が指定されても、座標系は変わりません。イメージング可能領域は、ページの内容をレンダリングするときに、プリンタが印刷できない領域まで広がることがないようにするためのものです。
print
に渡されるグラフィック・コンテキストにはクリッピング領域の情報が含まれていて、この情報では、イメージング可能領域の中で描画する必要のある部分が記述されています。印刷システムが必要なクリッピング処理を行うため、コンテキストにページ全体を描画しても常に安全です。ただし、クリッピング領域を使ってレンダリングされる範囲を制限することで、ページの中の印刷されない部分までレンダリングすることによるオーバーヘッドを除くことができます。グラフィックス・コンテキストからクリッピング領域を取得するには、Graphics.getClip
を呼び出します。クリッピング領域を使ってレンダリングのオーバーヘッドを減らすことを、強くお薦めします。
ページがレンダリングされている間でもユーザーがアプリケーションと対話を続けることができるよう、印刷操作をすべて「バックグラウンド」で起動することが望ましい場合があります。そのためには、独立したスレッドでPrinterJob.print
を呼び出します。
copyArea
、setXOR
、合成などのように、前のイメージの内容についての情報が必要なグラフィックス操作は、できるかぎり避ける必要があります。このような操作を行うと、レンダリングが遅くなり、結果の整合性が保たれない場合があります。
Printable
ジョブは、印刷を行うもっとも簡単な方法です。使うページ・ペインタは1つだけで、アプリケーションはPrintable
インタフェースを実装する単一のクラスを提供します。印刷を行うときは、印刷システムがページ・ペインタのprint
メソッドを呼び出して、各ページをレンダリングします。ページのインデックスは0から始まり、ページは順番どおりに要求されます。ただし、次のページに進む前に、各ページのレンダリング要求が何回もページ・ペインタに対して行われる場合があります。最後のページが印刷されると、ページ・ペインタのprintメソッドはNO_SUCH_PAGEを返します。
Printable
ジョブの場合:
PageFormat
を使います。印刷ダイアログを表示する場合、印刷システムはページ数の情報を利用できないため、ドキュメントのページ数は表示されません。Pageable
ジョブは、Printable
ジョブより柔軟性があります。Printable
ジョブのページとは異なり、Pageable
ジョブのページはレイアウトと実装が違っていてもかまいません。Pageable
ジョブを管理するには、Book
クラスを使うか、または独自にPageable
クラスを実装します。Pageable
を使うと、印刷システムは、印刷するページ数、各ページで使うページ・ペインタ、および各ページで使うPageFormat
を特定できます。決まった構造と書式を持つドキュメントを印刷する必要があるアプリケーションは、Pageable
ジョブを使う必要があります。
Pageable
ジョブの場合:
PageFormats
を使うことができます。Pageable
ジョブでは、ドキュメントのページ数をあらかじめ把握しておく必要はありません。ただし、Printable
ジョブとは異なり、どのような順序でもページをレンダリングできなければなりません。順序指定は飛んでいる場合があり、印刷システムは、次のページの印刷に移る前に、同じページのレンダリングを複数回要求することがあります。たとえば、ドキュメントの2ページ目と3ページ目の印刷を要求する場合、呼出しで要求されるページのインデックスが2、2、1、1、1という順序になることもあります。アプリケーションは、印刷ジョブが完了するまでの一連の手順に沿って、PrinterJob
オブジェクトを制御します。次に示すのは、アプリケーションで使用されるもっとも簡単な手順です。
PrinterJob.getPrinterJob
を呼び出して、新しいPrinterJob
オブジェクトを取得します。PageFormat
を決めます。デフォルトのPageFormat
を取得するには、defaultPage
を呼び出します。ユーザーが書式を指定できるようにダイアログ・ボックスを表示するには、pageDialog
を呼び出します。PrinterJob
に印刷されるジョブの特性を指定します。Printable
ジョブの場合は、setPrintable
を呼び出します。Pageable
ジョブの場合は、setPageable
を呼び出します。setPageable
に渡すにはBook
オブジェクトが最善です。printDialog
を呼び出し、ダイアログ・ボックスをユーザーに提示します。これはオプションです。このダイアログの内容と表示形式は、プラットフォームやプリンタの種類により異なります。大部分のプラットフォームでは、ユーザーは、このダイアログ・ボックスでプリンタの選択を変更できます。ユーザーが印刷ジョブをキャンセルすると、printDialog
メソッドからFALSE
が返されます。Printerjob.print
を呼び出して、ジョブを印刷します。このメソッドでは、適切なページ・ペインタのprint
が呼び出されます。次の場合、印刷の途中でジョブが中断することがあります。
PrinterException
のスロー - print
メソッドがこの例外をキャッチすると、ジョブは停止します。ページ・ペインタは、致命的なエラーを検出すると、PrinterException
をスローします。PrinterJob.cancel
の呼び出し - 印刷処理のループが終了され、ジョブがキャンセルされます。ダイアログ・ボックスを表示し、ボックスのボタンをクリックすることでユーザーが印刷をキャンセルできるようにするには、独立したスレッドでダイアログ・ボックスを表示し、cancel
メソッドを呼び出します。印刷ジョブが停止する前に生成されたページは、印刷される場合とされない場合があります。
一般に、print
メソッドから戻った時点では、印刷ジョブは完了していません。プリンタ・ドライバ、プリンタ・サーバー、またはプリンタ自体での処理がまだ行われているのが普通です。PrinterJob
オブジェクトの状態は、印刷されている実際のジョブの状態を反映しない場合があります。
PrinterJob
の状態はライフ・サイクル中に変化するため、特定のメソッドを特定のタイミングで呼び出すと不正になります。たとえば、print
を呼び出したあとでsetPageable
を呼び出しても無意味です。不正な呼出しを検出すると、PrinterJob
はjava.lang.IllegalStateException
をスローします。
Java印刷APIでは、ユーザー・インタフェース用のダイアログをアプリケーションが明示的に呼び出すことが要求されます。このようなダイアログは、プラットフォーム(Windowsなど)またはJDKの実装で提供される場合があります。対話型アプリケーションの場合、このようなダイアログを使うのが一般的です。ただし、バッチ用印刷アプリケーションの場合は、ダイアログは必要ありません。たとえば、夜間にデータベースのレポートを自動的に生成して印刷する場合などは、ダイアログの表示は不要です。ユーザーの介入を必要としない印刷ジョブのことを、サイレント印刷ジョブと呼ぶ場合があります。
PageFormat
に含まれるページ設定情報をユーザーが変更できるようにするには、ページ設定ダイアログを表示します。ページ設定ダイアログを表示するには、PrinterJob.pageDialog
を呼び出します。ページ設定ダイアログは、pageDialog
に渡すパラメータで初期化されます。ユーザーがダイアログの「OK」ボタンをクリックすると、PageFormat
のインスタンスが複製されて、ユーザーの選択に従って変更されたあと、メソッドから返されます。ユーザーがダイアログでキャンセルを選択した場合は、変更されていない元のPageFormat
がpageDialog
から返されます。
通常、メニューの印刷項目または印刷ボタンが起動されると、アプリケーションはユーザーに対して印刷ダイアログを表示します。印刷ダイアログを表示するには、PrinterJobのprintDialog
メソッドを呼び出します。PrinterJob
に提供されているPrintable
またはPageable
のページ数とページ書式に基づいて、ダイアログでのユーザーの選択が制限されます。印刷ダイアログでユーザーが「OK」をクリックすると、printDialog
からはTRUE
が返されます。ユーザーが印刷ダイアログでキャンセルを選択すると、メソッドからはFALSE
が返され、印刷ジョブが破棄されたとみなされます。
基本的な印刷処理は、次の手順で行います。
Printable
インタフェースを実装し、印刷する各ページをレンダリングできるページ・ペインタを提供する。PrinterJob
を作成する。setPrintable
を呼び出し、ドキュメントの印刷方法をPrinterJob
に伝える。PrinterJob
オブジェクトのprint
を呼び出して、ジョブを開始する。次の例では、Printable
ジョブを使って5ページを印刷し、各ページに緑色でページ番号を付けます。ジョブの制御はmain
メソッドで行い、このメソッドでPrinterJob
を取得して制御します。レンダリングは、ページ・ペインタのprint
メソッドで行われます。
import java.awt.*; import java.awt.print.*; public class SimplePrint implements Printable { private static Font fnt = new Font("Helvetica",Font.PLAIN,24); public static void main(String[] args) { // Get a PrinterJob PrinterJob job = PrinterJob.getPrinterJob(); // Specify the Printable is an instance of SimplePrint job.setPrintable(new SimplePrint()); // Put up the dialog box if (job.printDialog()) { // Print the job if the user didn't cancel printing try { job.print(); } catch (Exception e) { /* handle exception */ } } System.exit(0); } public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException { // pageIndex 0 to 4 corresponds to page numbers 1 to 5. if (pageIndex >= 5) return Printable.NO_SUCH_PAGE; g.setFont(fnt); g.setColor(Color.green); g.drawString("Page " + (pageIndex+1), 100, 100); return Printable.PAGE_EXISTS; } }
ページ・ペインタのprintメソッドの中でGraphics2D
の機能を実行するには、最初にGraphics
コンテキストをGraphics2D
にキャストします。
次の例では、ページ番号を赤と緑のグラデーションでレンダリングします。そのために、GradientPaint
がGraphics2D
コンテキストで設定されています。
import java.awt.*; import java.awt.print.*; public class SimplePrint2D implements Printable { private static Font fnt = new Font("Helvetica",Font.PLAIN,24); private Paint pnt = new GradientPaint(100f, 100f, Color.red, 136f, 100f, Color.green, true); public static void main(String[] args) { // Get a PrinterJob PrinterJob job = PrinterJob.getPrinterJob(); // Specify the Printable is an instance of SimplePrint2D job.setPrintable(new SimplePrint2D()); // Put up the dialog box if (job.printDialog()) { // Print the job if the user didn't cancel printing try { job.print(); } catch (Exception e) { /* handle exception */ } } System.exit(0); } public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException { // pageIndex 0 to 4 corresponds to page numbers 1 to 5. if (pageIndex >= 5) return Printable.NO_SUCH_PAGE; Graphics2D g2 = (Graphics2D) g; // Use the font defined above g2.setFont(fnt); // Use the gradient color defined above g2.setPaint(pnt); g2.drawString("Page " + (pageIndex+1), 100f, 100f); return Printable.PAGE_EXISTS; } }
ページ・ペインタのprintメソッドが同じページに対して繰返し呼び出される場合、メソッドはそのたびに同じ出力を生成しなければなりません。
同じページに対して繰返しレンダリング要求があるたびに、常に同じ出力を生成する方法はいくつもあります。たとえば、テキスト・ファイルの特定のページを印刷システムが要求するたびに、同じ出力が生成されるようにするには、ページ・ペインタで、ページごとのファイル・ポインタを格納して再使用したり、実際のページ・データを格納したりします。
次の例では、テキスト・ファイルの「リスト表示」が印刷されています。ファイルの名前は、main
メソッドに引数として渡されています。PrintListingPainter
クラスは、レンダリングを要求された新しいページの開始位置で、使われているファイル・ポインタを格納します。同じページをふたたびレンダリングするときは、ファイル・ポインタを記憶してある位置にリセットします。
import java.awt.*; import java.awt.print.*; import java.io.*; public class PrintListing { public static void main(String[] args) { // Get a PrinterJob PrinterJob job = PrinterJob.getPrinterJob(); // Ask user for page format (e.g., portrait/landscape) PageFormat pf = job.pageDialog(job.defaultPage()); // Specify the Printable is an instance of // PrintListingPainter; also provide given PageFormat job.setPrintable(new PrintListingPainter(args[0]), pf); // Print 1 copy job.setCopies(1); // Put up the dialog box if (job.printDialog()) { // Print the job if the user didn't cancel printing try { job.print(); } catch (Exception e) { /* handle exception */ } } System.exit(0); } } class PrintListingPainter implements Printable { private RandomAccessFile raf; private String fileName; private Font fnt = new Font("Helvetica", Font.PLAIN, 10); private int rememberedPageIndex = -1; private long rememberedFilePointer = -1; private boolean rememberedEOF = false; public PrintListingPainter(String file) { fileName = file; try { // Open file raf = new RandomAccessFile(file, "r"); } catch (Exception e) { rememberedEOF = true; } } public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException { try { // For catching IOException if (pageIndex != rememberedPageIndex) { // First time we've visited this page rememberedPageIndex = pageIndex; // If encountered EOF on previous page, done if (rememberedEOF) return Printable.NO_SUCH_PAGE; // Save current position in input file rememberedFilePointer = raf.getFilePointer(); } else raf.seek(rememberedFilePointer); g.setColor(Color.black); g.setFont(fnt); int x = (int) pf.getImageableX() + 10; int y = (int) pf.getImageableY() + 12; // Title line g.drawString("File: " + fileName + ", page: " + (pageIndex+1), x, y); // Generate as many lines as will fit in imageable area y += 36; while (y + 12 < pf.getImageableY()+pf.getImageableHeight()) { String line = raf.readLine(); if (line == null) { rememberedEOF = true; break; } g.drawString(line, x, y); y += 12; } return Printable.PAGE_EXISTS; } catch (Exception e) { return Printable.NO_SUCH_PAGE;} } }
Pageable
ジョブは、ドキュメントの体裁を1ページずつ明示的に構成するアプリケーションに適しています。Book
クラスはPageables
を手軽に使うための手段ですが、Book
が目的に合わない場合は、Pageable
の構造を独自に作ることもできます。ここでは、Book
の使用方法を説明します。
いくぶん複雑にはなりますが、印刷システムの柔軟性が増すため、Printable
ジョブよりもPageable
ジョブの方が実用に適しています。Pageables
の大きな利点は、通常はドキュメントのページ数を把握でき、印刷ダイアログ・ボックスでユーザーに対して表示できることです。これによりユーザーは、ジョブが正しく指定されているか確認したり、印刷するページの範囲を選択したりできます。
Book
は、ページの集合を表します。Bookの中のページは、同じサイズや向き、または同じページ・ペインタを共有する必要はありません。たとえば、1つのBook
で、2ページは縦方向のレター・サイズ、1ページは横方向のレター・サイズであってもかまいません。
Book
の最初の作成時には、このオブジェクトは空の状態です。Book
にページを追加するには、append
メソッドを使います。このメソッドは、ページのサイズ、印刷可能領域、向きを定義するPageFormat
オブジェクトと、Printable
インタフェースを実装するページ・ペインタを受け取ります。
Book
の複数のページで、同じページ書式とページ・ペインタを共有できます。append
メソッドの3番目のパラメータでページ数を指定すると、appendはオーバーロードされて、同じ属性の一連のページを追加できるようになります。
Book
の総ページ数がわからない場合は、append
メソッドにUNKNOWN_NUMBER_OF_PAGES
を渡すことができます。このようにすると、印刷システムは、NO_SUCH_PAGE
が返されるまで、ページのインデックスを増やしながらページ・ペインタを呼び出します。
setPage
メソッドを使うと、ページのページ書式またはページ・ペインタを変更できます。変更するページの識別には、Book
でのそのページの位置を示すページ・インデックスを使います。
印刷ジョブを準備するには、setPageable
を呼び出してBook
を渡します。setPageable
メソッドとsetPrintable
メソッドは、一緒には使用できません。つまり、PrinterJob
を準備するときは、どちらか一方だけを呼び出すようにします。
次の例では、Book
を使って、最初の簡単な印刷例と同じものを生成しています。この例はごく単純なものなので、Printable
ジョブの代わりにPageable
ジョブを使うことにそれほど利点はありませんが、Book
の基本的な使用法を示しています。この場合も、Printable
インタフェースを実装し、ページ・ペインタのprint
メソッドでページをレンダリングする必要があることに注意してください。
import java.awt.*; import java.awt.print.*; public class SimplePrintBook implements Printable { private static Font fnt = new Font("Helvetica",Font.PLAIN,24); public static void main(String[] args) { // Get a PrinterJob PrinterJob job = PrinterJob.getPrinterJob(); // Set up a book Book bk = new Book(); bk.append(new SimplePrintBook(), job.defaultPage(), 5); // Pass the book to the PrinterJob job.setPageable(bk); // Put up the dialog box if (job.printDialog()) { // Print the job if the user didn't cancel printing try { job.print(); } catch (Exception e) { /* handle exception */ } } System.exit(0); } public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException { g.setFont(fnt); g.setColor(Color.green); g.drawString("Page " + (pageIndex+1), 100, 100); return Printable.PAGE_EXISTS; } }
次の例では、表紙と本文に対して、2つの異なるページ・ペインタが使われています。表紙は横置きモードで印刷し、本文は縦置きモードで印刷しています。
import java.awt.*; import java.awt.print.*; public class PrintBook { public static void main(String[] args) { // Get a PrinterJob PrinterJob job = PrinterJob.getPrinterJob(); // Create a landscape page format PageFormat pfl = job.defaultPage(); pfl.setOrientation(PageFormat.LANDSCAPE); // Set up a book Book bk = new Book(); bk.append(new PaintCover(), pfl); bk.append(new PaintContent(), job.defaultPage(), 2); // Pass the book to the PrinterJob job.setPageable(bk); // Put up the dialog box if (job.printDialog()) { // Print the job if the user didn't cancel printing try { job.print(); } catch (Exception e) { /* handle exception */ } } System.exit(0); } } class PaintCover implements Printable { Font fnt = new Font("Helvetica-Bold", Font.PLAIN, 72); public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException { g.setFont(fnt); g.setColor(Color.black); int yc = (int) (pf.getImageableY() + pf.getImageableHeight()/2); g.drawString("Widgets, Inc.", 72, yc+36); return Printable.PAGE_EXISTS; } } class PaintContent implements Printable { public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException { Graphics2D g2 = (Graphics2D) g; int useRed = 0; int xo = (int) pf.getImageableX(); int yo = (int) pf.getImageableY(); // Fill page with circles or squares, alternating red & green for (int x = 0; x+28 < pf.getImageableWidth(); x += 36) for (int y = 0; y+28 < pf.getImageableHeight(); y += 36) { if (useRed == 0) g.setColor(Color.red); else g.setColor(Color.green); useRed = 1 - useRed; if (pageIndex % 2 == 0) g.drawRect(xo+x+4, yo+y+4, 28, 28); else g.drawOval(xo+x+4, yo+y+4, 28, 28); } return Printable.PAGE_EXISTS; } }
目次|前|次 |