SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Javaの標準機能だけで実現する帳票印刷

プリンタの検索と選択、そして特に長形3号封筒への印刷

Javaの標準機能だけで実現する帳票印刷 第3回

(4)Linuxでトレイを切り替える方法

 前回も触れましたが、Linuxの場合はJavaからプリンタへトレイの切り替えの指示ができません。選択したプリンタのデフォルトのトレイの用紙を使おうとするという不具合があります。例えば、用紙のカセット(Javaの用語ではトレイ)が2つある EPSON PX-1700FにA4とA3の用紙をセットして、デフォルトを用紙サイズA4、ソース(カセットのこと)を自動選択にしておいて、A3に印刷しようとするとA4に縮小印刷をします。

 Java以外のアプリケーションソフト、例えばLibreOfficeなどからの印刷では、用紙サイズの設定に合わせてトレイを自動で切り替えてくれますから、OSやプリンタドライバの問題ではなく、Javaに問題があるのだと推測できます。

 私がとっている回避策は、CUPSなどを使って同じプリンタを複数登録をして、それぞれに異なる用紙をデフォルト用紙サイズに設定し、印刷時に異なるプリンタとして選択するというものです。問題なく印刷できます。同じプリンタの複数登録はLinuxではとても簡単にできますが、Windows 10では簡単にはできないようです。USBとWiFiのようにポートが異なる接続を同時にできるものならば可能かもしれません。ただWindowsではJavaからトレイの切り替え指示ができますからその必要はないでしょう。

 MP493一台を複数のプリンタとしてデフォルト用紙を変えて登録すると、「PrinterSupported.java」の実行結果は次のようになります。ダイアログでCanon_MP493_chou3を選択した時の出力です。

実行時の端末表示(Linux)
adachi@debian64:~$ java PrinterSupported 0
lookup-count: 7
Canon_MP493_A4 media-count: 146
119.944 234.95	:Japanese long envelope #3	:83	:0.055999756+0.05000305+0.0=0.10600281
Canon_MP493_chou3 media-count: 146
119.944 234.95	:Japanese long envelope #3	:83	:0.055999756+0.05000305+0.0=0.10600281
Canon_MP493_hagaki media-count: 146
119.944 234.95	:Japanese long envelope #3	:83	:0.055999756+0.05000305+0.0=0.10600281
Canon_MP493_hagaki_fuchinashi media-count: 146
119.944 234.95	:Japanese long envelope #3	:83	:0.055999756+0.05000305+0.0=0.10600281
Canon_MP493_series media-count: 146
119.944 234.95	:Japanese long envelope #3	:83	:0.055999756+0.05000305+0.0=0.10600281
Canon_MP493_you2 media-count: 146
119.944 234.95	:Japanese long envelope #3	:83	:0.055999756+0.05000305+0.0=0.10600281
EPSON_EPSON_PX-1700F media-count: 30
120.015 234.985	:Envelope 120 x 235 mm	:887	:0.01499939+0.01499939+0.0=0.02999878
Canon_MP493_A4	:Japanese long envelope #3(83)...[1]
Canon_MP493_chou3	:Japanese long envelope #3(83)...[1]
Canon_MP493_hagaki	:Japanese long envelope #3(83)...[1]
Canon_MP493_hagaki_fuchinashi	:Japanese long envelope #3(83)...[1]
Canon_MP493_series	:Japanese long envelope #3(83)...[1]
Canon_MP493_you2	:Japanese long envelope #3(83)...[1]
EPSON_EPSON_PX-1700F	:Envelope 120 x 235 mm(887)...[1]
Only one or canceled so set to :Japanese long envelope #3
>>selected :Canon_MP493_chou3 :Japanese long envelope #3
Supported true
>>set to :Canon_MP493_chou3
 :Japanese long envelope #3 :portrait

(5)プリンタのデフォルトの用紙サイズでプリンタを選ぶ

 PrintServiceのメソッドにgetDefaultAttributeValue()というものがあります。これでデフォルトの用紙が長形3号になっているプリンタを探すということができます。前項にあるように同じプリンタを複数登録してデフォルトの用紙を替えておけば、実質上トレイを自動で切り替えることになります。

GetPrinterByDefaultSize.java
import java.util.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;
import javax.swing.JOptionPane;
import static javax.swing.JOptionPane.*;
/**
 用紙の幅x、高さy(y>=x)がデフォルトになっているプリンタを返す。
 複数あるときはダイアログで選択させて返す。
 引数にするPrintRequestAttributeSetには次のものをaddする。
 1.MediaSiazeName
 2.MediaPrintableArea (フィールド変数を使用)
 これは陽には戻値としない
 from PrinterSupported07.java
 2019-08-22 adachi
*/
public class GetPrinterByDefaultSize {
    /**フィールド変数は余白と用紙の大きさを比較するときの許容誤差である。
     現在はただの定数だが、将来的に変更のためのメソッドを加えられるようにしている。*/
    private float L = 10.1f;
    private float R = 10.2f;
    private float T = 10.3f;
    private float B = 10.4f;
    private float diff = 2f;
    public boolean GETMAPDUMP = false;
    public boolean RESULTDUMP = false;
    /** インスタンス化して、用紙の幅x、高さy(y>=x)、PrintRequestAttributeSetを引数に
     printerSetTo()を使ってサービスを得る。MediaSiazeNameなどはSetに加えられている。*/
    public static void main(String[] args) {
        int job = 4;
        float x=210f;
        float y=297f;
        switch (args.length) {
        case 2:
            x = Float.parseFloat(args[0]);
            y = Float.parseFloat(args[1]);
            break;
        case 1:
            job = Integer.parseInt(args[0]);
            if(job==0) {x=120f; y=235f;} //chou_3
            if(job==1) {x=100f; y=148f;} //hagaki
            if(job==2) {x=240f; y=332f;} //kaku_2
            if(job==3) {x=297f; y=420f;} //A3
            if(job==4) {x=210f; y=297f;} //A4
            if(job==5) {x=162f; y=229f;} //kaku_6
            if(job==6) {x=98f; y=190f;} //you_6
            break;
        default:
            System.out.println("usage: java GetPrinterByDefaultSize n");
            System.out.println(" 0:chou3 1:hagaki 2:kaku2 3:A3");
            System.out.println(" 4:A4    5:kaku6  6:you6 ");
            System.out.println("or java GetPrinterByDefaultSize x y ...");
       }
        PrintRequestAttributeSet rqset = new HashPrintRequestAttributeSet();
        //rqset.add(OrientationRequested.REVERSE_LANDSCAPE);
        GetPrinterByDefaultSize supp = new GetPrinterByDefaultSize();
        supp.GETMAPDUMP = true;
        supp.RESULTDUMP = true;
        PrintService sv = supp.printerSetTo(x,y,rqset);
        if (sv!=null) {
            System.out.println(">>set to :"+sv.getName());
            checkSet(rqset);
        }else{
            System.out.println(">>canceled");
        }
    }
    /** デバグ用 main()からのみ呼ばれる。*/
    public static void checkSet(PrintRequestAttributeSet set1){
        Media med1 = (Media)set1.get(Media.class);
        System.out.print(" :"+med1.toString());
        OrientationRequested ori1 = 
            (OrientationRequested)set1.get(OrientationRequested.class);
        System.out.println((ori1!=null)?" :"+ori1.toString():" :portrait");
    }
    
    /**  用紙の幅x、高さy(y>=x)がデフォルトになっているプリンタを返す。
    複数あるときはダイアログで選択させて返す。
    引数のPrintRequestAttributeSetには、デフォルトのMediaSizeNmaeと余白をadd()する。
    PrintRequestAttributeSet は明示的には返り値にしていないが、メソッド内の変更はメソッドから
    戻った後に有効になっている。これはServiceUI.printDialog()の振る舞いに合わせたものである。*/
    public PrintService printerSetTo(float x, float y, PrintRequestAttributeSet rqset){
        PrintService sv = null;
        Map<PrintService,MediaSizeName> svmap = getMap1(x,y);
        if(svmap.size()>0){
            PrintService[] ssvs = svmap.keySet().toArray(new PrintService[0]);
            if(svmap.size()>1){
                sv = (PrintService)showInputDialog(
                    null, //parent,
                    "使用するプリンタを選んでください",//message,
                    "プリンタの選択",//title,
                    JOptionPane.QUESTION_MESSAGE, //msgTp,
                    null, //icon,
                    ssvs, ssvs[0]
                      );
                if (sv == null) return sv;
            }else if(svmap.size()==1){
                sv = ssvs[0];
            }
            MediaSizeName msn = svmap.get(sv);
            rqset.add(msn);
            MediaSize msize = MediaSize.getMediaSizeForName(msn);
            float mwidth  = msize.getX(MediaPrintableArea.MM);
            float mheight = msize.getY(MediaPrintableArea.MM);
            MediaPrintableArea mparea = new MediaPrintableArea(
                L, T, (mwidth - L - R), (mheight - T - B),
                MediaPrintableArea.MM);
            rqset.add(mparea);
            if(RESULTDUMP){
                System.out.print(">>selected :"+sv.getName());
                Media med = (Media)rqset.get(Media.class);
                System.out.println(" :"+med.toString());
                System.out.println(
                    "Supported "+ sv.isAttributeValueSupported(med, null, rqset));
            }
        }else{
            showMessageDialog(
                    null,//parent,
                    x+" × "+y+" の用紙がデフォルトのプリンタがありません",//message,
                    "残念な報告",//title
                    JOptionPane.ERROR_MESSAGE //msgTp
                  );
            if(RESULTDUMP){
                System.out.print("no printer for : "+x+" × "+y);
            }
            return sv;
        }
        return sv;
    }
    /** (x,y)の用紙がデフォルトになっているサービスをkeyとし、
    その用紙のMediaSizeNameを値とするマップを返す
    lookupPrintServices()とgetDefaultAttributeValue()を使って作成する。
    printerSetTo()から呼ばれるメソッド */
    public Map<PrintService,MediaSizeName> getMap1(float x, float y){
        PrintService[] svs = PrintServiceLookup.lookupPrintServices(null,null);
        if (GETMAPDUMP) System.out.println("lookup-count: "+svs.length);
        Map<PrintService,MediaSizeName> svmap = new LinkedHashMap<>();
        for (PrintService sv : svs) {
            Media media = (Media)sv.getDefaultAttributeValue(Media.class);
            if(media instanceof MediaSizeName){
                MediaSizeName msn = (MediaSizeName)media;
                MediaSize ms = MediaSize.getMediaSizeForName(msn);
                if(ms!=null){
                    float msx = ms.getX(MediaSize.MM);
                    float msy = ms.getY(MediaSize.MM);
                    if(diff>=Math.abs(msx-x) && diff>=Math.abs(msy-y)){
                        if (GETMAPDUMP){
                            System.out.println(
                                msx+" "+msy+"\t:"
                                +msn.toString()+"("+msn.getValue()+")" 
                                +"\t:"+sv.getName());
                        }
                        svmap.put(sv,msn);
                    }
                }
            }
        }
        return svmap;
    }
}

まとめとプログラム一覧

今回のまとめ

 プリンタの選択はPrintServiceLookup.lookupPrintServices()とServiceUI.printDialog()を組み合わせる方法がよく紹介されているが、条件にあったプリンタを特定するにはあまり役に立たない。

 PrintServic#getSupportedAttributeValues()を使えばプリンタドライバから情報を得ることができる。これを使ってプリンタと利用可能用紙サイズを用紙の縦横の長さから選び出す「PrinterSupported.java」を作った。得られたプリンタとMediaSizeNameをペアで使って、PrinterJobまたはDocPrintJobの好みの方で印刷できる。

 LinuxではJavaからプリンタのトレイを切り替えることができないが、用紙サイズごとにプリンタを登録し、getDefaultAttributeValue()メソッドを利用することで、トレイを切り替えることが可能になる。

プログラム一覧

  • PrinterSelectTest01.java
    条件にあったプリンタを検索して選択する一般的なプログラム
  • PrinterSelectTest02.java
    接続されたプリンタから、あらかじめ分かっている名前でプリンタを選択して使う
  • SupportedValuesTest.java
    すべてのプリンタについて、getSupportedAttributeValues()を使い、指定の大きさに近い用紙のMediaSizeNameの一覧を取得する。
  • PrinterSupportedTest.java
    用紙の幅x、高さy(y>=x)に、適合するプリンタを調べServiceUI.printDialog()で選択させて返す。
  • PrinterSupported.java
    用紙の幅x、高さy(y>=x)に、適合するプリンタを調べ独自なダイアログで選択させて返す。
  • SomethingPrintable02.java
    PrinterSupportedクラスを使って選択したプリンタ(と用紙)とPrinterJobを使う帳票印刷の典型例
  • WithDocPrintJob.java
    PrinterSupportedクラスを使って選択したプリンタ(と用紙)とDocPrintJobを使う帳票印刷の典型例
  • GetPrinterByDefaultSize.java
    用紙の幅x、高さy(y>=x)がデフォルトになっているプリンタを返す。複数あるときはダイアログで選択させて返す。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
Javaの標準機能だけで実現する帳票印刷連載記事一覧

もっと読む

この記事の著者

安達 順一(アダチ ジュンイチ)

私立高校に理科・情報の教員として勤めていました。Linuxサーバー/クライアントの授業システムを作り、移動プロファイルで運用していました。教員用にもLinuxサーバーを用意し成績処理プログラムを書きました。情報の学校設定科目ではウェブページ制作とjavaのプログラミングの初歩の授業を作りました。情報...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/11671 2020/06/22 11:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー