はじめに
第2回の最後に書いたように、プリンタの検索と選択が必要ない状況も考えられます。Javaによるプリンタの選択にはいろいろと複雑な問題があるので、必要がなければ触らない方が良いかもしれません。ここではJISの封筒を使いたいとか、A4とA3を別のプリンタから出すようになっていて自動で切り替えるようにしたいとか、問題を抱えたときのために解説をします。OSとプリンタの機種の組み合わせで環境依存が大きいと思うので、必要なものを選択できるように段階を踏んで説明します。
- (1)条件に合致したプリンタを選択する一般的な方法。
 - (2)JISの封筒を指定する方法。
 - (3)その手法を使って、条件に合致したプリンタを検索する実効性のある方法。
 - (4)Linuxでトレイを切り替える方法。
 - (5)プリンタのデフォルトの用紙サイズでプリンタを選ぶ方法。
 
(1)条件に合致したプリンタを選択する一般的な方法
PrintServiceLookupクラスのlookupPrintServices()というstaticメソッドを使います。PrintRequestAttributeSetと、印刷データの種類を表すDocFlavorを引数をとり、それらを扱うことができるプリンタの一覧を得ることができるとAPI仕様に書いてあります。 分かりやすい例で言えばA3が使えるプリンタだけを探し出すことができるというわけです。
Linuxの場合、A3に限定するというのはうまく働きます。しかし、Windows 10ではA3を指定してもA4までしか使えないプリンタも入ってしまいます。さらに、Windows 10ではA4を指定するとOneNoteというプリンタでないものがでてきたりします。これは検索する対象がサービスですから正当ではありますがプリンタの選択には邪魔になります。
また、葉書を指定した場合には葉書以上の用紙サイズを印刷できるプリンタが選択されるだけで、現在葉書がカセットに入っていたり手差しに入っていたりするプリンタを探し出すところまで期待することはできません。
というわけで、プリンタの配列が得られても、その中の一つを自動で選んでそのまま印刷するわけには行かず、得られた配列を候補としてダイアログを出し、手動で切り替えるようにすることになるでしょう。それが、ServiceUI.printDialog()です。
つまり、lookupPrintServices()で得られた「条件を満たす」プリンタ群から、ServiceUI.printDialog()を使ってプリンタを選択します。概略はこんな感じです(実はあまり役に立ちません)。
import java.awt.print.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;
/** 条件にあったプリンタを検索して選択する一般的なプログラム */
public class PrinterSelectTest01{
    public static void main(String[] args) {
        PrintRequestAttributeSet rqset = new HashPrintRequestAttributeSet();
        rqset.add(MediaSizeName.ISO_A3); 
        DocFlavor flv = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
        PrintService[] svs = PrintServiceLookup.lookupPrintServices(flv,rqset);
        for(PrintService sv:svs){
            System.out.println(sv.getName());
        }
        PrintService service = ServiceUI.printDialog(
            null, 200, 200, svs, svs[0], flv, rqset);
        PrinterJob pj = PrinterJob.getPrinterJob();
        if (service != null) {
            try{
                pj.setPrintService(service);
            }catch (PrinterException e) {
                System.err.println(e);
            }
        }
        PrintService selectedprnter = pj.getPrintService();
        System.out.println("---> "+selectedprnter.getName());
    }
}
  DocFlavorは帳票印刷の場合DocFlavor.SERVICE_FORMATTED.PRINTABLEを指定します。これは2Dグラフィクスで直接描く文書であることを意味します。
PrintRequestAttributeSetには必要な印刷属性を加えます。ここではA3を指定しています。
この2つのインスタンス、flv, rqsetを引数にして、lookupPrintServices()は条件に合致したプリンタを検索し、結果を配列svsとして返しています。確認のためsvs内のプリンタ名を書き出しています。
この中から使用するプリンタを選択するためにServiceUI.printDialog()を使います。引数は次のとおりです。
- null,200,200 : デフォルトのスクリーンのx=200,y=200の位置に表示する。
 - svs : 選択候補となるPrintServiceの配列。空の配列でもよいが、nullは不可。
 - svs[0] : 選択候補の初期値。nullは不可。
 - flv, rqset : 検索に使ったものと同様。ダイアログにサイズと余白を表示するために加える。
 
実行結果(Windows 10)
実行した環境ではA3の使えるプリンタは EPSON PX-1700F と Canon Inkjet iP9910 だけですが、たくさん出てくることが確認できます。GUIからMP493を選択したので、最終行に「---> Canon MP493 series Printer」と表示されています。
PS Z:\java\print03> java PrinterSelectTest01 Microsoft XPS Document Writer Microsoft Print to PDF EPSON PX-1700F Canon MP493 series Printer Canon Inkjet iP9910 ---> Canon MP493 series Printer
実行結果(Linux)
LinuxではA3プリンタのEPSON PX-1700Fだけが選択肢になります。Linuxには Canon Inkjet iP9910 のドライバがなく、そもそも接続していません。 GUIからPX-1700Fを選択したので、「---> EPSON_EPSON_PX-1700F」と表示されています。
adachi@debian64:~$ java PrinterSelectTest01 EPSON_EPSON_PX-1700F ---> EPSON_EPSON_PX-1700F
一般的な方法の問題点
この概略に沿って解説したものを、あちらこちらで目にしますが、いろいろな問題が残っていることに言及しているものに出会ったことがありません。筆者の使い方が悪くて問題が出るということではないと思っていますがどうでしょう。
第一に、先に示したとおり、WindowsではAttributeSetの制約に従わず全部のプリンタを拾ってしまいます。実はこれはA4プリンタでもA3をプリンタ(ドライバ)側で縮小して印刷する機能があって、A3印刷ができると答えているのが原因のようです。
第二に、これもWindowsだけですが、AttributeSetにMediaPrintableAreaの指定が入っていると、lookupPrintServices()はしばらく考え込んで、空の配列を返します。よく目にする解説では、lookupPrintServices()の引数のAttributeSetを、nullにしたり、生成しただけで何もadd()していないものを使っているので、これに遭遇することはありません。しかし、MediaPrintableAreaを指定しないとダイアログで余白がデフォルトのままです。これには回避策があって、lookupのAttributeにMediaPrintableAreaを指定しないで、ServiceUI.printDialogの時のAttributeに追加指定すれば、選択もうまく行って(制約は効きませんが)、ダイアログに余白が正しく入ります。
第三は、不具合ではありませんが、ServiceUI.printDialogのプリンタの初期値に何を持ってくるかの問題です。Windowsの場合にプリンタでないサービスも候補の配列に入るので、それがsvs[0]に入ってしまうと結局変更しなければならなくなります。この対策からか、svs[0]の代わりにPrintServiceLookup.lookupDefaultPrintService()で得るデフォルトプリンタを指定している例をよく見ますが、A3が使えるプリンタを探している時に、デフォルトのプリンタがA3を使えない物だったら必ず変更しなければならなくなって不合理です。
とにかくここまでの使い方では、使用できるプリンタを検索して選択の候補を絞り込むという機能はないに等しいということになります。OSやプリンタ機種、ドライバに依るかもしれませんし、今後改良があるかもしれませんので、再検証のためにこのプログラムを残しておきます。
プリンタが分かっている場合
「様式Aの帳票はプリンタAから印刷する、様式Bの帳票はプリンタBから印刷する」などとプリンタを固定できる場合には自動で切り替えることも可能です。帳票印刷の設計では、こちらのほうが役に立つかもしれません。
次に示すのは、あらかじめ調べておいた"Canon_MP493_series"という名前のプリンタを指定するというプログラムです。Windowsでは"Canon MP493 series"とアンダースコアでなくスペースで区切るという違いがありますから、共通の文字列である"MP493"で探しています。これで見つからなければデフォルトのプリンタになります。
import java.awt.print.*;
import javax.print.*;
/**接続されたプリンタから MP493 という名前のものを使う*/
public class PrinterSelectTest02{
   public static void main(String[] args) {
        String pname = "MP493";
        PrinterJob pj = PrinterJob.getPrinterJob();
        PrintService foundprn = getServiceByName(pname);
        if(foundprn!=null){
            try{
                pj.setPrintService(foundprn);
            }catch (PrinterException e) {
                    System.err.println(e);
            }
        }
        PrintService prnter = pj.getPrintService();
        System.out.println("---> "+prnter.getName());
    }
    public static PrintService getServiceByName(String name){
        PrintService prnter = null;
        PrintService[] svs = PrinterJob.lookupPrintServices();
        for(PrintService sv:svs){
            System.out.println(sv.getName());
            if(sv.getName().contains(name)) prnter = sv;
        }
        return prnter;
    }
}
  実行結果(Windows 10)
プリンタサービスの一覧を表示してから、自動でMP493を選択しています。A3限定のときに比べて一覧のサービスの数が増えています。
PS Z:\java\print03> java PrinterSelectTest02 OneNote Microsoft XPS Document Writer Microsoft Print to PDF Fax EPSON5AE8BC (PX-1700F) EPSON PX-1700F Canon MP493 series Printer Canon Inkjet iP9910 ---> Canon MP493 series Printer
実行結果(Linux)
同じプリンタでも、プリンタ名は環境ごとに異なるものです。
adachi@debian64:~$ java PrinterSelectTest02 Canon_MP493_series EPSON_EPSON_PX-1700F ---> Canon_MP493_series
