| 目次|前|次 |
アプリケーションでは、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; } }
| 目次|前|次 |