はじめに
第1回でJavaの基本機能だけで文字と罫線を0.1mm単位で位置指定して印刷する方法を紹介しました。0.1mmと書いたのは、72dpiだから解像度が不足するという誤解を解くためです。内部的にはマイクロメートル(µm)単位で扱われているようですので解像度は十分です。
今回は、用紙のサイズや向き、余白の指定について説明します。プリンタは、あらかじめデフォルトのプリンタとして登録されているものを使用することを想定します。もちろん印刷ダイアログでデフォルト以外のプリンタに変更することは可能です。帳票印刷の用途では対象のプリンタはあらかじめ決まっている場合が多いでしょうからプリンタ選択はこの範囲で十分だと思います。
Javaには指定した用紙が使えるプリンタを探し出して選択する方法が備わっていることになっていますが、WindowsでA3を印刷できるプリンタを探すとA4プリンタもリストに含まれてしまいます。他にも一般的に解説されていない不具合と思われるものや謎の仕様があるようですので、プリンタの検索と選択については次回に回します。
Oracle社のサイトにあるドキュメントの中で、印刷についてまとめて書いてある一番新しいものは、「Java印刷サービスAPIユーザー・ガイド」です。これはJavaに印刷サービスが追加されたバージョン1.4の時に書かれたものです。まだSun Microsystems社の時代であったころの機能追加ですからかなり昔ですが、Javaの印刷はその時からほとんど変わっていません。最新のJava 12も全部を確認したわけではありませんが変わっていないようです。手直しをしたほうが良い部分もあるように感じられますが、変更がないということは同じ手法を使い続けられるということでもあり、これはこれで喜ばしいことでもあります。
Oracle社のJava印刷サービスAPIユーザー・ガイドへのリンクを示しておきます。このリンクはJava 8の日本語版です。今回の連載のプログラムはJava 8で検証していますが12でも問題なくコンパイルできるはずです。Java 8での検証にした理由は仕様が変わっていないことに加え、LinuxのOpenJDKの多くが現在は8であることと、WindowsにおいてもJRE(実行環境)は8であることが多いことからです。
帳票印刷に使える2つの方法
「Java印刷サービスAPIユーザー・ガイド」では「2Dグラフィックスを印刷する」として書かれているものが、これからやろうとする帳票印刷に相当します。PrinterJobを使う方法とDocPrintJobを使う方法が説明されています。2つの方法を大雑把に表にしてみます。
方法1 | 方法2 | |
---|---|---|
直接印刷に使うクラス | PrinterJob | DocPrintJob |
正式らしい名称 | Java 2D印刷API | Java印刷サービスAPI |
組み合わされる機能 |
PageFormat |
AttributeSet DocFlavor PrintServiceLookup ServiceUI |
導入年とVer. | 1998年 J2SE1.2 | 2002年 J2SE1.4 |
PrinterJobとDocPrintJobは、名前にDocがついているか否かが大きな違いですが、PrinterとPrintの違いもあります。この解説では触れませんが、さらに古い方法でPrintJobというクラスが残っていますので注意が必要です。
この2つは、Javaの印刷に関する2回の仕様拡張により、それぞれ導入されたものなのですが、拡張の目的が異なります。PreinterJobが導入された時の目玉はグラフィックの解像度の向上でした。そこで'2D'がつきました。DocPrintJobの目玉は必要な機能を持ったプリンタ(印刷サービス)を見つけ出して印刷する機能です。そのためにどんなものを印刷するのか(Doc)とどんな要求をするのか(Request)を伝える方法が必要になりました。これが、DocFlavorとAttributeSetです。
このようにAttributeSetは、DocPrintJobで印刷するために導入された仕組みですが、同時にPreinterJobも拡張されて、こちらでも使えるようになりました。表に(AttributeSet)と括弧づきなのはこれを表現したつもりです。PrinterJobに組み合わせるAttributeSetは、中心的な役割をしているPrintRequestAttributeSetですが、これによって用紙の指定や余白の指定にPaperやPageFormatを使う必要がなくなり、とても簡単になっています。
プリンタの検索と選択はPrintServiceLookupとServiceUIというクラスで行います。これもDocPrintJobで印刷するために導入された仕組みですが、PrintreJobにも後からServiceを結びつけるメソッドを追加しているので、PrintreJobとPrintServiceLookup、ServiceUIを組み合わせて利用できるようになっています。
という事情で、PrinterJobを使う方法もDocPrintJobを使う方法も、帳票印刷の場合はどちらでも同じように使えます。
プリンタの選択を次回に回した関係で、今回は、PrinterJobを使う方法で説明します。DocPrintJobは選択したServiceから生成するものだからです。しかし、PrintRequestAttributeSetについては使い方が同じですから、後からDocPrintJobに替えることは簡単です。
DocPrintJobには、2Dグラフィックスを印刷する他に、画像ファイルやPDFファイルのデータを直接印刷する機能があります。「Java印刷サービスAPIユーザー・ガイド」では、これを新機能として強調して解説していますが、この連載でやろうとしているのは2Dグラフィックスによる帳票印刷の方です。
PrinterJobでの印刷
第1回で紹介したPrinterJobでの印刷はデフォルトプリンタにデフォルトな用紙設定で印刷するものでした。これに、用紙サイズや余白の指定を加えます。
「SomethingPrintable00.java」で用いた方法はこうでした。
Printable pable = new SomethingPrintable00(); PrinterJob pj = PrinterJob.getPrinterJob(); pj.setPrintable(pable); if (pj.printDialog()) { try { pj.print(); } catch (PrinterException e) { System.err.println(e); } }
これにPrintRequestAttributeSetを使って用紙サイズや余白を指定するには、次のようにします。
Printable pable = new SomethingPrintable00(); //用紙サイズなどの指定をrqsetに集約 PrintRequestAttributeSet rqset = new HashPrintRequestAttributeSet(); rqset.add(new Copies(3)); //3部印刷 rqset.add(MediaSizeName.ISO_A3); //A3用紙 rqset.add(new MediaPrintableArea( 10.1f, 10.3f, 276.7f, 399.3f, MediaPrintableArea.MM)); //左余白,上余白,描画幅,描画高,単位 PrinterJob pj = PrinterJob.getPrinterJob(); pj.setPrintable(pable); //rqsetを指定してダイアログ提示と印刷 if (pj.printDialog(rqset)) { try { pj.print(rqset); } catch (PrinterException e) { System.err.println(e); } }
これだけで、デフォルトプリンタまたはダイアログで変更するプリンタが、A3用紙で印刷できるなら指示通り印刷されます。
指示はA3の用紙に左余白10.1mm、上余白10.3mm、印刷幅276.7mm、印刷高399.3mmで同じものを3枚印刷するというものです。このような解説に同じものを複数枚という指定はよく目にします。最も説明することが少なく確実に反映される例だからでしょう。
printDialog()をPrintRequestAttributeSetを引数にして使うと、「ページ設定」のタブが使えます。このタブで用紙サイズと余白(マージン)が伝わっていることを確認できます。
プログラム中では印刷幅276.7mmとしていましたが、これはA3の短辺が297.0mmなので、297.0-10.1-10.2=276.8として計算して指定したものです。Javaはここから297.0-10.1-276.7=10.2と逆算して右マージンを計算しています。
環境によってはプリンタがA3に対応しない場合にマージンを少なく調整して印刷領域を確保しようとするかもしれません。そのときはデフォルトのプリンタをA3に対応したものにしておくことで問題を回避できます。
PrintRequestAttributeSetの指定内容については後でもう少し解説します。
ちなみに上記のプログラムの例は、前回の「SomethingPrintable00.java」を変更することを想定していますが、実際には下記のimport文の追加が必要になります。
import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.standard.Copies; import javax.print.attribute.standard.MediaPrintableArea; import javax.print.attribute.standard.MediaSizeName;
面倒なら次のようにしてもいいでしょう。
import javax.print.attribute.*; import javax.print.attribute.standard.*;