(1)Printableなクラス
ここからプログラムの説明になります。まず(1)Printableなクラスを作って、(2)PrinterJobで印刷する手順を説明し、実際の文字や線の描画はその後にします。
Printableインターフェースを持つクラスの大枠は次のようなものです。
public class SomethingPrintable00 implements Printable {
@Override
public int print(Graphics g, PageFormat pf, int pageIndex) {
if (pageIndex != 0) return NO_SUCH_PAGE;
Graphics2D g2 = (Graphics2D)g;
/*.....
ここに実際の文字や線の描画が入ります
g2.drawString("印刷する文字列", x位置, y位置); //文字の印字
g2.drawLine(始点x,始点y,終点x,終点y); //線の描画
.....*/
return PAGE_EXISTS;
}
}
Printableはprint()というメソッドを持たねばなりません。引数もこの3つに決まっています。このメソッドは印刷時にシステムによって呼び出されます。自分で呼び出すことはありません。
pageIndexにページ番号が入って呼び出されます。ページ番号は0から始まります。今回は1ページしか書きませんから、pageIndex==0のときは字を描いたり線を引いたりしたあと、PAGE_EXISTSを返します。0でないときはNO_SUCH_PAGEを返します。
システムは、NO_SUCH_PAGEが返されるまでpageIndexを増加させながらprint()メソッドを呼び出します。一度NO_SUCH_PAGEが返されるとそれ以上呼び出しません。
PAGE_EXISTS、NO_SUCH_PAGEは、Printableのフィールドにあるintの定数です。実際の値はPAGE_EXISTS=0、NO_SUCH_PAGE=1ですが値を知っている必要はありません。
Graphicsは、描画対象や描画色やフォントなど、描画操作に必要な状態情報をまとめたクラスです。 このインスタンスが引数でgという名前で与えられるので、これに対して、色やフォントの設定を変更したり、文字を描いたり、線を引いたりするメソッドを使って描画していきます。
歴史的な事情で、Graphics2D g2 = (Graphics2D)g;を最初に書きます。引数には、Graphicsと書いてありますが、実際には拡張されたGraphics2Dのインスタンスが渡されてくるので、Graphics2DにキャストしておくことでGraphicsクラスのメソッドと、拡張されたGraphics2Dのメソッドの両方を使うことができます。
PageFormatには印刷されるページのサイズと印刷方向がまとめられています。
帳票印刷では、決められた大きさの用紙に印刷するよう設計しますから、印刷時に印字領域のサイズを取得して大きさを調整するという使い方はしないでしょう。ここでも使っていません。
(2)PrinterJobで印刷
mainに書かれたものが、Printableなクラスから印刷物を得る方法です。
mainの中にある必要はありません。帳票印刷などではデータを読み込んだり、計算したりのプログラムの中から、呼び出すことになろうかと思いますが、いまは複雑な準備がいらないのでmainの中に入れてしまったということです。
ここに示した方法は、用紙や余白の指定を省いて、最低限の指示だけにしました。コンピュータに設定されたデフォルトのプリンタのデフォルトな用紙に印刷されます。
PrinterJob pj = PrinterJob.getPrinterJob();
Printable pable = new SomethingPrintable00();
pj.setPrintable(pable);
if (pj.printDialog()) {
try { pj.print(); }
catch (PrinterException e) {
System.err.println(e);
}
}
手順としては4ステップです。
-
PrinterJobのインスタンスを作って名前をつける(ここではpj)
昔の自分を思い出すとこれがなかなか腑に落ちなかった。何もないところから突然作れるなら、作らなくても使えるようにしておけばいいのに……と。形式的にはPrinterJobクラス内のスタティックメソッドでPrinterJobクラスのインスタンスを作るということでPrinterJobクラスが見慣れてくると、理解できないことはない。 -
Printableなクラスのインスタンスを渡す
new SomethingPrintable()で、Printableなクラスのインスタンスを作りpableとし、これを引数としてPrinterJobのインスタンス(pj)にsetPrintableメソッドで渡している。 -
印刷ダイアログを出す
pj.printDialog()でダイアログが表示される。ユーザーがキャンセルしなければtrueが返る。falseが返った場合は印刷しないようにしている。印刷ダイアログは省略できる。その場合はデフォルトのプリンタのデフォルト設定で印刷される。 -
印刷の指示を出す
pj.print()のことだが、もちろんprint()はPrintableなクラスのメソッドではなく、PrinterJobのメソッドである。例外を捉えるためにtry-catchに入れなければならない。
用紙のサイズ、向き、余白などを指定したいときには、もう少し複雑になります。別に項目を立てて改めて解説します。
ちなみに、印刷について調べると、java.awt.PrintJobを使う方法が解説されていることがあります。PrinterJobと名前が似ているので最初は混乱しました。ここで使っているのはjava.awt.print.PrinterJobの方です。後にもう一つ紹介するのは、javax.print.DocPrintJobを使う方法です。Docがついている分印象が違いますが、DocPrinterJobではありません。注意が必要です。
長さの単位
メソッドで使われる長さの単位は、1/72インチ(=1ポイント)です。これを整数で指定するのでは72dpiにしかなりません。これではページプリンタの解像度が600dpiとすると不足します。しかし、Graphics2Dではfloatやdoubleで位置を指定できます。小数で指定すれば、0.1ポイント単位で指定するだけでも720dpiということになります。floatでも十進6桁強の精度がありますから十分です。
換算は(mmの値)÷25.4×72=(ptの値)なので、72/25.4fの値を用意して掛け算することにします。
float mm2pt = 72/25.4f;
Graphics2Dのメソッドにはfloatを使うものとdoubleを使うものがありますが、floatのみというメソッドがありますからfloatに統一しておきます。精度的には十分でしょう。
ちなみに精度を計算しておきましょう。Javaでfloatの最小値は1.40239846×10-45です。これが分解できる最小値です。単位はポイントなのでmmに換算するには72で割って25.4を掛けます。およそ4.94735×10-46mmです。原子の大きさが10-8mm程度ですから、かなりのオーバースペックということになります。
Java API仕様ドキュメントのMediaPrintableAreaの項には、「印刷可能領域属性の値が、整数型としてマイクロメートル(µm)単位で内部に格納される」とありますから、実際には内部的な描画の精度もµm単位なのかもしれません。
