目次 | 前へ | 次へ |
アプリケーションでは、Java 印刷 API を使って次の処理が可能です。
これらの機能の中には、Java™ 2 SDK の印刷 API と実装ではサポートされていないものもあります。将来的には、これらの機能をすべてサポートするように API が拡張される予定です。たとえば、アプリケーションが制御できる印刷ジョブの名前付きプロパティーのセットを増やすことで、別のプリンタ制御機能が追加されます。
Java 印刷 API は、コールバック印刷モデルに基づいています。このモデルでは、アプリケーションではなく印刷システムが、ページを印刷するときの制御を行います。アプリケーションは、印刷するドキュメントについての情報を提供し、印刷システムは、各ページのレンダリングが必要になると、それをアプリケーションに要求します。
印刷システムは、特定のページのレンダリングを 2 回以上要求したり、実際とは異なる順序でページのレンダリングを要求したりする場合があります。印刷システムからどのページが要求されても、アプリケーションは正しいページイメージを生成できなければなりません。この点、印刷システムはウィンドウツールキットと似ています。ウィンドウツールキットは、いつでも、どのような順序でも、コンポーネントに再描画を要求できます。
コールバック印刷モデルは、従来のアプリケーション駆動の印刷モデルよりも柔軟性があり、より広範なシステムとプリンタでの印刷に対応しています。たとえば、逆の順序で出力ページがスタックされるプリンタの場合、印刷システムはアプリケーションに逆の順序でページを生成するように要求することで最終のスタックを正しい読み取り順序にすることができます。
このモデルを使うアプリケーションは、十分なメモリーまたはディスク領域がないためページ全体のビットマップをバッファリングできないコンピュータからでも、ビットマッププリンタに印刷できます。この場合、ページは一連の小さなビットマップ、つまりバンドとして印刷されます。たとえば、1 ページの 10 分の 1 をバッファリングできるだけのメモリーしか利用できない場合、ページは 10 個のバンドに分割されます。印刷システムは、アプリケーションに対し、ページごとにレンダリングを 10 回要求し、1 回で 1 つのバンドを埋めます。アプリケーションは、バンドの数またはサイズを意識する必要はありません。要求されたときにそのページをレンダリングできれば十分です。
アプリケーションは、印刷機能をサポートするために、次の 2 つのタスクを行う必要があります。
ユーザーは通常、アプリケーションのボタンをクリックしたりメニュー項目を選択したりして、印刷を開始します。ユーザーが印刷操作を起動すると、アプリケーションは、PrinterJob
オブジェクトを作成し、それを使って印刷処理を管理します。
アプリケーションは、印刷ジョブを設定し、ユーザーに対して印刷ダイアログを表示し、印刷処理を開始します。
ドキュメントを印刷する場合、アプリケーションは印刷システムからの要求に対して各ページをレンダリングする必要があります。このメカニズムをサポートするため、アプリケーションは、Printable
インタフェースを実装したページペインタを提供します。印刷システムでページのレンダリングが必要になると、印刷システムはページペインタの print
メソッドを呼び出します。
ページペインタの print
メソッドが呼び出されると、メソッドにはページイメージのレンダリングに使う Graphics
コンテキストが渡されます。また、ページの幾何学的レイアウトを指定する PageFormat
オブジェクトと、印刷ジョブ内でのページの順番を示す整数のページインデックスも渡されます。
印刷システムは、Graphics
と Graphics2D
の両方のレンダリングをサポートしています。Java 2D™ の Shapes
、Text
、および Images
を印刷するには、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 など) または Java™ 2 SDK の実装で提供される場合があります。対話型アプリケーションの場合、このようなダイアログを使うのが一般的です。ただし、バッチ用印刷アプリケーションの場合は、ダイアログは必要ありません。たとえば、夜間にデータベースのレポートを自動的に生成して印刷する場合などは、ダイアログの表示は不要です。ユーザーの介入を必要としない印刷ジョブのことを、サイレント印刷ジョブと呼ぶ場合があります。
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; } }
目次 | 前へ | 次へ |