CUPSとJava印刷:用紙の選択

OSはDebian9(Strech)64bit、CUPS 2.2.1、プリンタはCanon MP493、javac 1.8.0_181 で用紙の選択がうまくできない問題。数年前から取り組んでいるのでOSやソフトウェアのバージョン、プリンタ機種によらず同様だと言える。Windowsでは同様の問題があるかどうかすら調べていない。

PrintRequestAttributeSetで用紙の余白や向きを指定して印刷するのは可能。

printDialog(pset)で指定した向きと余白がダイアログに表示される。予め指定したAttributeをここで変更して印刷に反映させることができる。

ところが用紙の指定、つまりA4とか葉書とか長3封筒とかの指定が無視される。予め指定したものがダイアログに出てくるがそのとおりにならない。

ブラウザで設定する、CUPSのDefaultOptionsで指定した用紙が使われる。

まとめ

調査過程をそのまま書いているので冗長になっている。結論を先に書いておく。解決としては完全な理解ではないので参照が必要な場合も考えられる。

CUPSで同じプリンタをDefaultを変えて複数別名で登録し、javaからサービスを名前から選択して印刷することで解決。

サービスを検索する方法は大雑把に2つあるが、まだ違いが見えない。この方法のためにはどちらでも良い。

Java8なのはDebian9のリポジトリでのバージョンが8だから。ただしMediaSizeNameあたりの様子はJava10でもAPI仕様を見る限り変化はない。

確認のためのプログラム(一部)

MediaSizeNameやMediaSizeの調査。

必要があったら見てください。下で結果をまとめています

		PrintRequestAttributeSet prset = new HashPrintRequestAttributeSet();
		//MediaSizeName msname = MediaSizeName.JIS_B5;
		//MediaSizeName msname = MediaSizeName.ISO_A4;
		MediaSizeName msnameP = MediaSizeName.JAPANESE_POSTCARD;
		MediaSize msfnmpost = MediaSize.getMediaSizeForName(msnameP);
		System.out.println("(1)MediaSizeName.JAPANESE_POSTCARD");
		System.out.println("getMediaSizeName()="+msfnmpost.getMediaSizeName());
		//System.out.println("getName()="+msfnmpost.getName());
		System.out.println("toString()="+msfnmpost.toString());
		System.out.println("getX()="+msfnmpost.getX(MediaPrintableArea.MM));
		System.out.println("getY()="+msfnmpost.getY(MediaPrintableArea.MM));

		MediaSizeName msnameA4 = MediaSizeName.ISO_A4;
		MediaSize msfnmA4 = MediaSize.getMediaSizeForName(msnameA4);
		System.out.println("(2)MediaSizeName.ISO_A4");
		System.out.println("getMediaSizeName()="+msfnmA4.getMediaSizeName());
		//System.out.println("getName()="+msfnmA4.getName());
		System.out.println(msfnmA4);
		System.out.println("getX()="+msfnmA4.getX(MediaPrintableArea.MM));
		System.out.println("getY()="+msfnmA4.getY(MediaPrintableArea.MM));

		MediaSize msothp = MediaSize.Other.JAPANESE_POSTCARD;
		System.out.println("(3)MediaSize.Other.JAPANESE_POSTCARD");
		System.out.println("getMediaSizeName()="+msothp.getMediaSizeName());
		//System.out.println("getName()="+msothp.getName());
		System.out.println("toString()="+msothp.toString());
		System.out.println("getX()="+msothp.getX(MediaPrintableArea.MM));
		System.out.println("getY()="+msothp.getY(MediaPrintableArea.MM));

		MediaSize mscho3 = MediaSize.JIS.CHOU_3;
		System.out.println("(4)MediaSize.JIS.CHOU_3");
		System.out.println("getMediaSizeName()="+mscho3.getMediaSizeName());
		//System.out.println("getName()="+mscho3.getName());
		System.out.println("toString()="+mscho3.toString());
		System.out.println("getX()="+mscho3.getX(MediaPrintableArea.MM));
		System.out.println("getY()="+mscho3.getY(MediaPrintableArea.MM));

		MediaSize msisoa4 = MediaSize.ISO.A4;
		System.out.println("(5)MediaSize.ISO.A4");
		System.out.println("getMediaSizeName()="+msisoa4.getMediaSizeName());
		//System.out.println("getName()="+msisoa4.getName());
		System.out.println("toString()="+msisoa4.toString());
		System.out.println("getX()="+msisoa4.getX(MediaPrintableArea.MM));
		System.out.println("getY()="+msisoa4.getY(MediaPrintableArea.MM));

		MediaSize msmyhaga = new MediaSize(100.0f,148.0f,MediaPrintableArea.MM);
		System.out.println("(6)new MediaSize(100.0f,148.0f,MediaPrintableArea.MM)");
		System.out.println("getMediaSizeName()="+msmyhaga.getMediaSizeName());
		//System.out.println("getName()="+msmyhaga.getName());
		System.out.println("toString()="+msmyhaga.toString());
		System.out.println("getX()="+msmyhaga.getX(MediaPrintableArea.MM));
		System.out.println("getY()="+msmyhaga.getY(MediaPrintableArea.MM));

		MediaSize msmycho3 = new MediaSize(120.0f,235.0f,MediaPrintableArea.MM);
		System.out.println("(7)new MediaSize(120.0f,235.0f,MediaPrintableArea.MM)");
		System.out.println("getMediaSizeName()="+msmycho3.getMediaSizeName());
		//System.out.println("getName()="+msmycho3.getName());
		System.out.println("toString()="+msmycho3.toString());
		System.out.println("getX()="+msmycho3.getX(MediaPrintableArea.MM));
		System.out.println("getY()="+msmycho3.getY(MediaPrintableArea.MM));
		
		MediaSize msmya4 = new MediaSize(210.0f,297.0f,MediaPrintableArea.MM);
		System.out.println("(8)new MediaSize(210.0f,297.0f,MediaPrintableArea.MM)");
		System.out.println("getMediaSizeName()="+msmya4.getMediaSizeName());
		//System.out.println("getName()="+msmya4.getName());
		System.out.println("toString()="+msmya4.toString());
		System.out.println("getX()="+msmya4.getX(MediaPrintableArea.MM));
		System.out.println("getY()="+msmya4.getY(MediaPrintableArea.MM));

		MediaSizeName mssname = MediaSize.findMedia(120.0f,235.0f,MediaPrintableArea.MM);
		System.out.println("(11)"+mssname);
		mssname = MediaSize.findMedia(100.0f,148.0f,MediaPrintableArea.MM);
		System.out.println("(12)"+mssname);
		mssname = MediaSize.findMedia(210.0f,297.0f,MediaPrintableArea.MM);
		System.out.println("(13)"+mssname);
	}

実行結果

(1)MediaSizeName.JAPANESE_POSTCARD
getMediaSizeName()=japanese-postcard
toString()=100000x148000 um
getX()=100.0
getY()=148.0
(2)MediaSizeName.ISO_A4
getMediaSizeName()=iso-a4
210000x297000 um
getX()=210.0
getY()=297.0
(3)MediaSize.Other.JAPANESE_POSTCARD
getMediaSizeName()=japanese-postcard
toString()=100000x148000 um
getX()=100.0
getY()=148.0
(4)MediaSize.JIS.CHOU_3
getMediaSizeName()=null
toString()=120000x235000 um
getX()=120.0
getY()=235.0
(5)MediaSize.ISO.A4
getMediaSizeName()=iso-a4
toString()=210000x297000 um
getX()=210.0
getY()=297.0
(6)new MediaSize(100.0f,148.0f,MediaPrintableArea.MM)
getMediaSizeName()=null
toString()=100000x148000 um
getX()=100.0
getY()=148.0
(7)new MediaSize(120.0f,235.0f,MediaPrintableArea.MM)
getMediaSizeName()=null
toString()=120000x235000 um
getX()=120.0
getY()=235.0
(8)new MediaSize(210.0f,297.0f,MediaPrintableArea.MM)
getMediaSizeName()=null
toString()=210000x297000 um
getX()=210.0
getY()=297.0
(11)null
(12)japanese-postcard
(13)iso-a4

必要なattribute

MediaSizeName
A4とかPostcardとかの名前
MediaPrintableArea
MediaSizeから用紙の幅と高さを得て、左マージン,上マージン,印字幅、印字高さで指定する
用紙の向き
OrientationRequested.PORTRAIT などの定数で指定する。

MediaSizeNameから

A4の時にいろいろ調べて、MediaSizeNameから設定をスタートしていた。

今回使う可能性のあるのは、葉書、長3封筒、A4。

MediaSizeNameが定義されているのは、MediaSizeName.JAPANESE_POSTCARD, MediaSizeName.ISO_A4 の2つ。長3はない。

MediaSizeNameがあれば、MediaSizeから、X,Yを得られる

MediaSize	MediaSize.getMediaSizeForName(MediaSizeName media)
float		MediaSize#getX(int units)
MediaSizeName⇨MediaSize⇨X,Y

MediaSizeは文字列でも数値でもないので、toString()で調べると、nm μm単位での用紙サイズが得られた。

葉書とA4についてはMediaSizeも得られ、これからMediaSizeNameを再取得できた。

MediaSizeName MediaSize MediaSizeName X,Y
指定 .toString()値 MediaSizeから再取得 getX,getY
MediaSizeName.JAPANESE_POSTCARD 100000x148000 μm japanese-postcard 100.0, 148.0
MediaSizeName に CHO_3はない
MediaSizeName.ISO_A4 210000x297000 μm iso-a4 210.0, 297.0

MediaSizeから

MediaSizeが定義されているのは、

MediaSize.Other.JAPANESE_POSTCARD, MediaSize.JIS.CHOU_3, MediaSize.ISO.A4 で、長3もある。

MediaSizeから、MediaSizeNameと、X,Yを得られることになっている。

MediaSizeName	MediaSize#getMediaSizeName()
float		MediaSize#getX(int units)
MediaSize⇨MediaSizeName
MediaSize⇨MediaSize⇨X,Y

しかし、やってみると、X,Yはすべて求まるが、MediaSizeNameは長3封筒ではうまく行かない

MediaSize MediaSizeName X,Y
指定 getMediaSizeName()で取得 getX,getY
MediaSize.Other.JAPANESE_POSTCARD japanese-postcard 100.0, 148.0
MediaSize.JIS.CHOU_3 null 120.0, 235.0
MediaSize.ISO.A4 iso-a4 210.0, 297.0

findMedia()もだめ

MediaSize.findMedia(120.0f, 235.0f, MediaPrintableArea.MM); というメソッドがあって、用紙のサイズからMediaSizeNameを求めるもの。これも葉書とA4ではうまく行くが、長3封筒ではだめ。

MediaSize.findMedia(100.0f,148.0f,MediaPrintableArea.MM); //japanese-postcard
MediaSize.findMedia(120.0f,235.0f,MediaPrintableArea.MM); //null
MediaSize.findMedia(210.0f,297.0f,MediaPrintableArea.MM); //iso-a4

CUPSのデフォルト設定

493をA4に設定し直す。(もとは長形3号封筒)

Canon_MP493_series
Canon_MP493_series (Idle, Accepting Jobs, Not Shared)
Description:	Canon MP493 series
Location:	
Driver:	Canon PIXUS MP493 - CUPS+Gutenprint v5.2.11 (color)
Connection:	usb://Canon/MP493%20series?serial=C0A92F&interface=1
Defaults:	job-sheets=none, none media=iso_a4_210x297mm sides=one-sided

ppdの変更箇所を調べる(.0がバックアップファイル)

root@debian64:/etc/cups/ppd# diff Canon_MP493_series.ppd Canon_MP493_series.ppd.O 
49c49
< *DefaultPageSize: A4
---
> *DefaultPageSize: w340h666
200c200
< *DefaultPageRegion: A4
---
> *DefaultPageRegion: w340h666
348c348
< *DefaultImageableArea: A4
---
> *DefaultImageableArea: w340h666
495c495
< *DefaultPaperDimension: A4
---
> *DefaultPaperDimension: w340h666
787c787
< *DefaultStpiShrinkOutput: Crop
---
> *DefaultStpiShrinkOutput: Expand

666は235mm÷25.4×72で72dpiでのドット数。

A4に設定時

A4でプログラムし、ダイアログでハガキに変えてみる

MediaSizeNameをA4に設定してそこから始めているプログラムである。

MediaSizeNameスタートでPrinterJobを使いダイアログまでで印刷はしない

    public void doDialogCheck(){
        PrintRequestAttributeSet prset = new HashPrintRequestAttributeSet();
        //MediaSizeName msname = MediaSizeName.JAPANESE_POSTCARD;
        MediaSizeName msname = MediaSizeName.ISO_A4;
        MediaSize mscho3 = MediaSize.JIS.CHOU_3;
        MediaSize msothp = MediaSize.Other.JAPANESE_POSTCARD;
        MediaSize msisoa4 = MediaSize.ISO.A4;
        //MediaSize ms = mscho3; //x
        //ms = msothp; //o
        //ms = msmycho3; //x
        //ms = msmyhaga; //o
        //ms = msmya4; //x
        //ms = msisoa4; //x
        MediaSize ms = MediaSize.getMediaSizeForName(msname); //今回はA4
        float mw = ms.getX(MediaPrintableArea.MM);
        float mh = ms.getY(MediaPrintableArea.MM);
        float leftmm = 5.1f;
        float rightmm = 4.9f;
        float topmm = 4.8f;  //landscape前のTOP
        float bottomm = 5.2f;
        prset.add(new MediaPrintableArea(
           leftmm, topmm,
           (mw - leftmm - rightmm),
           (mh - topmm - bottomm), MediaPrintableArea.MM));
        //MediaSizeName mssname = MediaSize.findMedia(mw,mh,MediaPrintableArea.MM);
        //System.out.println("(B)"+mssname);
        //if (msname!=null) prset.add(msname);
        prset.add(msname);
        prset.add(OrientationRequested.PORTRAIT);
        //prtreqattset0.add(OrientationRequested.REVERSE_LANDSCAPE);
        PrintQuality pq = PrintQuality.NORMAL;
        prset.add(pq);
        
        System.out.println(prset.size());//test
        Attribute[] atbs= prset.toArray();
        for (int i=0;prset.size()>i;i++){
          	System.out.println(atbs[i]);
        }
        PrinterJob pj = PrinterJob.getPrinterJob();
        pj.printDialog(prset);

        System.out.println(prset.size());//test
        atbs= prset.toArray();
        for (int i=0;prset.size()>i;i++){
          	System.out.println(atbs[i]);
        }
        //pj.printDialog();
    }

このプリンタが出るのはデフォルトプリンタだから

指定通りA4でマージンも指定通り

ここは設定できるものがない

ここを葉書にすると

プログラムの出力。

4
iso-a4
(5.1,4.8)->(200.0,287.0)mm
portrait
normal

葉書に設定したので2回目のダンプでこのようになる

5
japanese-postcard
(5.1,4.8)->(90.0,138.0)mm
portrait
normal
1

この逆も画面で見る限りはうまく行っている様に見える

しかし実際の印刷はこの指定のとおりにならず、A4全体に広がり文字は崩れる。

MediaSizeスタートでA4

MediaSizeからMediaSizeNameを得る方法は2つ。後者は大きさから近いものを選択してくれる期待をしたがそうではなかった。

MediaSize#getMediaSizeName()
MediaSize.findMedia(mw,mh,MediaPrintableArea.MM);

MediaSizeスタートでPrinterJobを使いダイアログまでで印刷はしない

   public void doDialogCheck(){
        PrintRequestAttributeSet prset = new HashPrintRequestAttributeSet();
        MediaSize ms;
        ms = msisoa4;
        float mw = ms.getX(MediaPrintableArea.MM);
        float mh = ms.getY(MediaPrintableArea.MM);
        float leftmm = 5.1f;
        float rightmm = 4.9f;
        float topmm = 4.8f;  //landscape前のTOP
        float bottomm = 5.2f;
        prset.add(new MediaPrintableArea(
           leftmm, topmm,
           (mw - leftmm - rightmm),
           (mh - topmm - bottomm), MediaPrintableArea.MM));
        MediaSizeName mssname = MediaSize.findMedia(mw,mh,MediaPrintableArea.MM);
        System.out.println("(B)"+mssname);
        if (mssname!=null) prset.add(mssname);
        prset.add(OrientationRequested.PORTRAIT);
        PrintQuality pq = PrintQuality.NORMAL;
        prset.add(pq);
        System.out.println(prset.size());//test
        Attribute[] atbs= prset.toArray();
        for (int i=0;prset.size()>i;i++){
            System.out.println(atbs[i]);
        }
        PrinterJob pj = PrinterJob.getPrinterJob();
        pj.printDialog(prset);

        System.out.println(prset.size());//test
        atbs= prset.toArray();
        for (int i=0;prset.size()>i;i++){
            System.out.println(atbs[i]);
        }
    }

名前も得られ、葉書への変更も可能(もちろんJava上に限っての話)

(B)iso-a4
4
iso-a4
(5.1,4.8)->(200.0,287.0)mm
portrait
normal
5
japanese-postcard
(5.1,4.8)->(90.0,138.0)mm
portrait
normal
1

MediaSizeスタートで長3号

長3号はMediaSizeNameがないのでMediaSizeスタートする必然がある。

MediaSize長3号スタートでPrinterJobを使いダイアログまでで印刷はしない

    public void doDialogCheck(){
        PrintRequestAttributeSet prset = new HashPrintRequestAttributeSet();
        MediaSize mscho3 = MediaSize.JIS.CHOU_3;
        MediaSize ms;
        ms = mscho3; //x
        float mw = ms.getX(MediaPrintableArea.MM);
        float mh = ms.getY(MediaPrintableArea.MM);
        float leftmm = 5.1f;
        float rightmm = 4.9f;
        float topmm = 4.8f;  //landscape前のTOP
        float bottomm = 5.2f;
        prset.add(new MediaPrintableArea(
           leftmm, topmm,
           (mw - leftmm - rightmm),
           (mh - topmm - bottomm), MediaPrintableArea.MM));
        MediaSizeName mssname = MediaSize.findMedia(mw,mh,MediaPrintableArea.MM);
        System.out.println("(B)"+mssname);
        if (mssname!=null) prset.add(mssname);
        prset.add(OrientationRequested.PORTRAIT);
        PrintQuality pq = PrintQuality.NORMAL;
        prset.add(pq);
        System.out.println(prset.size());//test
        Attribute[] atbs= prset.toArray();
        for (int i=0;prset.size()>i;i++){
            System.out.println(atbs[i]);
        }
        PrinterJob pj = PrinterJob.getPrinterJob();
        pj.printDialog(prset);

        System.out.println(prset.size());//test
        atbs= prset.toArray();
        for (int i=0;prset.size()>i;i++){
            System.out.println(atbs[i]);
        }
    }

長3が見つからないのでA4になっていて、マージンも狂っている。

選択肢にJapanese long envelope #3 があるので選択してみる。

プログラムの出力では、Java側ではnullなので指定しないでダイアログを呼び出す。ただし用紙の大きさは得られているので、Setには正しく入力されている。(110.0,225.0)はマージンを引いた長3の値。

(B)null
3
portrait
(5.1,4.8)->(110.0,225.0)mm
normal

しかし、ダイアログには長3を設定しなかった分、A4になり、マージンが狂うのではないか。ダイアログでJapanese long envelope #3 に設定後、戻った時のSetは次のようになる。(19.944,162.95)は不明。A4でもポストカードでもない。長3に設定しなければA4になる。

5
1
portrait
(5.1,4.8)->(19.944,162.95)mm
normal
Japanese long envelope #3

各用紙の名前と大きさ

JavaのマニュアルのMediaSizeNameには名前しかない。MediaSizeには長さが書いてあるので、必要なものだけ転記しておく。

すべて、static MediaSize なフィールド値

10x13などのxは大文字小文字で揺れがある。

MediaSize.ISO (主要なもののみ)MediaSizeName
A3ISOのA3サイズ横297 mm、縦420 mmISO_A3
A4ISOのA4サイズ横210 mm、縦297 mmISO_A4
DESIGNATED_LONGISO 指定のロングサイズ横 110 mm、縦 220 mmISO_DESIGNATED_LONG
MediaSize.NAMediaSizeName
LEGAL北米規格のリーガルサイズ横 8.5 inch、縦 14 inch NA_LEGAL
LETTER北米規格のレターサイズ横 8.5 inch、縦 11 inch NA_LETTER
NA_10x13_ENVELOPE北米規格の横 10 inch、縦 13 inch の封筒サイズNA_10x13_ENVELOPE
NA_10x14_ENVELOPE北米規格の横 10 inch、縦 14 inch の封筒サイズNA_10x14_ENVELOPE
NA_10X15_ENVELOPE北米規格の横 10 inch、縦 15 inch の封筒サイズNA_10X15_ENVELOPE
NA_5X7北米規格の横 5 inch、縦 7 inch の用紙NA_5X7_ENVELOPE
NA_6X9_ENVELOPE北米規格の横 6 inch、縦 9 inch の封筒サイズNA_6X9_ENVELOPE
NA_7X9_ENVELOPE北米規格の横 7 inch、縦 9 inch の封筒サイズNA_7X9_ENVELOPE
NA_8X10北米規格の横 8 inch、縦 10 inch の用紙NA_8X10_ENVELOPE
NA_9x11_ENVELOPE北米規格の横 9 inch、縦 11 inch の封筒サイズNA_9x11_ENVELOPE
NA_9x12_ENVELOPE北米規格の横 9 inch、縦 12 inch の封筒サイズNA_9x12_ENVELOPE
NA_NUMBER_10_ENVELOPE北米規格の 10 号ビジネス封筒サイズ横 4.125 inch、縦 9.5 inch NA_NUMBER_10_ENVELOPE
NA_NUMBER_11_ENVELOPE北米規格の 11 号ビジネス封筒サイズ横 4.5 inch、縦 10.375 inch NA_NUMBER_11_ENVELOPE
NA_NUMBER_12_ENVELOPE北米規格の 12 号ビジネス封筒サイズ横 4.75 inch、縦 11 inch NA_NUMBER_12_ENVELOPE
NA_NUMBER_14_ENVELOPE北米規格の 14 号ビジネス封筒サイズ横 5 inch、縦 11.5 inch NA_NUMBER_14_ENVELOPE
NA_NUMBER_9_ENVELOPE北米規格の 9 号ビジネス封筒サイズ横 3.875 inch、縦 8.875 inch NA_NUMBER_9_ENVELOPE
MediaSize.OtherMediaSizeName
EXECUTIVEエグゼクティブサイズ横 7.25 inch、縦 10.5 inch EXECUTIVE
FOLIOフォリオサイズ横 8.5 inch、縦 13 inch FOLIO
INVOICE請求書サイズ横 5.5 inch、縦 8.5 inch INVOICE
ITALY_ENVELOPEイタリアの封筒サイズ横 110 mm、縦 230 mm ITALY_ENVELOPE
JAPANESE_DOUBLE_POSTCARD日本の往復はがきサイズ横 200 mm、縦 148 mm JAPANESE_DOUBLE_POSTCARD
JAPANESE_POSTCARD日本のはがきサイズ横 100 mm、縦 148 mm JAPANESE_POSTCARD
LEDGER帳簿サイズ横 11 inch、縦 17 inch LEDGER
MONARCH_ENVELOPE封筒サイズ(キングサイズ) 横 3.87 inch、縦 7.5 inch MONARCH_ENVELOPE
PERSONAL_ENVELOPE封筒サイズ(パーソナルサイズ) 横 3.625 inch、縦 6.5 inch PERSONAL_ENVELOPE
QUARTOクアトロサイズ横 8.5 inch、縦 10.83 inch QUARTO
TABLOIDタブロイドサイズ横 11 inch、縦 17 inchTABLOID
MediaSize.JIS (封筒のみ)MediaSizeName
CHOU_1JISの長型1号封筒サイズ横142 mm、縦332 mm
CHOU_2JISの長型2号封筒サイズ横119 mm、縦277 mm
CHOU_3JISの長型3号封筒サイズ横120 mm、縦235 mm
CHOU_30JISの長型30号封筒サイズ横92 mm、縦235 mm
CHOU_4JISの長型4号封筒サイズ横90 mm、縦205 mm
CHOU_40JISの長型40号封筒サイズ横90 mm、縦225 mm
KAKU_0JISの角型0号封筒サイズ横287 mm、縦382 mm
KAKU_1JISの角型1号封筒サイズ横270 mm、縦382 mm
KAKU_2JISの角型2号封筒サイズ横240 mm、縦332 mm
KAKU_20JISの角型20号封筒サイズ横229 mm、縦324 mm
KAKU_3JISの角型3号封筒サイズ横216 mm、縦277 mm
KAKU_4JISの角型4号封筒サイズ横197 mm、縦267 mm
KAKU_5JISの角型5号封筒サイズ横190 mm、縦240 mm
KAKU_6JISの角型6号封筒サイズ横162 mm、縦229 mm
KAKU_7JISの角型7号封筒サイズ横142 mm、縦205 mm
KAKU_8JISの角型8号封筒サイズ横119 mm、縦197 mm
KAKU_A4JISの角型A4封筒サイズ横228 mm、縦312 mm
YOU_1JISの洋型1号封筒サイズ横120 mm、縦176 mm
YOU_2JISの洋型2号封筒サイズ横114 mm、縦162 mm
YOU_3JISの洋型3号封筒サイズ横98 mm、縦148 mm
YOU_4JISの洋型4号封筒サイズ横105 mm、縦235 mm
YOU_5JISの洋型5号封筒サイズ横95 mm、縦217 mm
YOU_6JISの洋型6号封筒サイズ横98 mm、縦190 mm
YOU_7JISの洋型7号封筒サイズ横92 mm、縦165 mm

なお、型の字を使っているが、形が正しいようだ。

MediaSizeNameにない名前

MediaSizeにあって、MediaSizeNameにないものは、JISの封筒関係の24件だけ。

逆にMediaSizeNameにあってMediaSizeにないものが、ISOのC0,C1,C2の3件。

MediaSizeには94、MediaSizeNameには73の登録がある。

近い紙

JIS.CHOU_3に近い大きさの封筒。Javaにあってもプリンタ(特に日本のだからか)に無い場合もある。

MediaSizeNameinchmm
MediaSize.JIS.CHOU_3120mm 235mm
MediaSize.Other.ITALY_ENVELOPE110mm 230mm
MediaSize.NA.NA_NUMBER_10_ENVELOPE横 4.125 inch、縦 9.5 inch 104.775mm 241.3mm
MediaSize.NA.NA_6X9_ENVELOPE横 6 inch、縦 9 inch 152.4mm 228.6mm

プリントサービスを使ってみる

PrintServiceのこと。

PrintRequestAttributeSetのインスタンスを観察する中、プリンタのもつ選択肢が反映されているようにも見えるが、確信が持てない。また、同じ選択肢が2つずつ現れる場合もある。

こちらで設定したPrintRequestAttributeSetを用いての印刷はこちらから一方的にリクエストすることになるイメージがある。

まだ使用経験があまりないが、プリントサービスを使って設定にあったプリンタを探すというアプローチなら、うまく要求が伝わるかもしれない。

ということで、やってみる。プログラムの流れは、

(1)設定したPrintRequestAttributeSetのインスタンスで、サービスをlookupする。
(2)0番を使って(defaultを使う試用例もあるが)ダイアログを出して確認
(3)選択したservice(0番)からDocPrintJobのインスタンスを生成し
(4)printableなクラス(自作)から生成したSimpleDocを用意し、
(5)DocPrintJobのインスタンスにとPrintRequestAttributeSetとともに渡してprintさせる

PrintServiceからDocPrintJobを作って印刷もするプログラム

    public void doDialogCheck(){
        DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
        PrintRequestAttributeSet prset = new HashPrintRequestAttributeSet();

        MediaSize mscho3 = MediaSize.JIS.CHOU_3;
        MediaSize msothp = MediaSize.Other.JAPANESE_POSTCARD; //ここから選択
        MediaSize msisoa4 = MediaSize.ISO.A4;
        MediaSize msotie = MediaSize.Other.ITALY_ENVELOPE;//no
        MediaSize msnan10 = MediaSize.NA.NA_NUMBER_10_ENVELOPE;
        MediaSize msna6x9 = MediaSize.NA.NA_6X9_ENVELOPE;
        MediaSize ms;
        ms = msothp; //ここを変更してテストする
        float mw = ms.getX(MediaPrintableArea.MM);
        float mh = ms.getY(MediaPrintableArea.MM);
        float leftmm = 5.1f;
        float rightmm = 4.9f;
        float topmm = 4.8f;  //landscape前のTOP
        float bottomm = 5.2f;
        prset.add(new MediaPrintableArea(
           leftmm, topmm,
           (mw - leftmm - rightmm),
           (mh - topmm - bottomm), MediaPrintableArea.MM));
        prset.add(OrientationRequested.PORTRAIT);
        MediaSizeName mssname = MediaSize.findMedia(mw,mh,MediaPrintableArea.MM);
        System.out.println("(A)"+mssname);
        if (mssname!=null) prset.add(mssname);
        System.out.println("(B)"+ms.getMediaSizeName());
        System.out.println("BeforeLookup : "+prset.size());//test
        Attribute[] atbs= prset.toArray();
        for (int i=0;prset.size()>i;i++){
            System.out.println(atbs[i]);
        }
        PrintService[] psvs = PrintServiceLookup.lookupPrintServices(flavor,prset);
        PrintService   dpsv = PrintServiceLookup.lookupDefaultPrintService();

        for (int i=0;psvs.length>i;i++){
            System.out.println("sv("+i+") "+psvs[i]);
        }

        PrintService service = 
            ServiceUI.printDialog(null, 400, 400,psvs, psvs[0], flavor, prset);

        System.out.println("AfterSelectService : "+prset.size());//test
        atbs= prset.toArray();
        for (int i=0;prset.size()>i;i++){
            System.out.println(atbs[i]);
        }

        if (service != null) {
            DocPrintJob job = service.createPrintJob();
            SimpleDoc doc = new SimpleDoc(new SomethingPrintable01(), flavor, null);
            try { 
                job.print(doc, prset);
            }catch (PrintException e){
                System.err.println(e.getMessage());
            }
        }
    }

印刷もするが、画面出力は次の通り

PrintRequestAttributeSetではJAPANESE_POSTCARD;を指定。
プリンタ(Canon_MP493_series)のCUPSデフォルトはA4。
PrintRequestAttributeSetの確認ではうまく行っているが、印刷はだめ。

(A)japanese-postcard
(B)japanese-postcard
BeforeLookup : 3
portrait
(5.1,4.8)->(90.0,138.0)mm
japanese-postcard
sv(0) IPP Printer : Canon_MP493_series
sv(1) IPP Printer : EPSON_EPSON_PX-1700F
AfterSelectService : 4
portrait
(5.1,4.8)->(90.0,138.0)mm
japanese-postcard
1

プリントサービスを使っても改善されない

印刷は、はみ出すとか、拡大縮小されるとか、位置が狂うとかではなく、話にならない。

あとで判明するが、次の項目を設定しなければ「Shrink」になっていて、ハガキがA4に拡大される。逆にプリンタの設定範囲以上に書かせようとすると縮小される。このため郵便番号に数字が合わなくなりCropに変更していたのが下記のスキャンを産んだ。Expandでも同様に点々になる。

Shrink Page If Necessary to Fit Borders:
・Shrink (print the whole page)
・Crop (preserve dimensions)
・Expand (use maximum page area)

どちらにしても、MediaSizeNameの問題のないA4と葉書ですでにうまくいかない。

LibreOfficeではA4デフォルトのプリンタに印刷できるし、EPSONのPX-1700FでA4,A3,B5の間でもあった(この時はShrinkだった)ことなので、OS,プリンタ,の問題ではなくJavaの問題ではないかと疑われる。

プリントサービスはリクエストに合致したサービスを検索するという触れ込みだったので、PrintRequestAttributeSetを先に用意したが、サービスを選んでから設定してprint時に指定する例が多く見られる。これはこうしなければうまく行かないということなのかもしれない。

用紙別にプリンタを用意する

MediaSizeNameに長形3号がないのも問題ではあるが、名前のあるものを使ってもJavaから用紙の変更ができないので、最終手段。A4,葉書、長形3号のそれぞれのプリンタの設定を用意してこれを切り替えることを試みる。

/etc/cups/ppdにファイルがある。これはプリンタの対応する用紙や解像度などの条件が書かれたテキストファイルで用紙のデフォルトもここに書かれている。上の「CUPSのデフォルト設定」に前回A4にしたときの様子がある。

root@debian64:~# ls -l /etc/cups
合計 72
-rw-r--r-- 1 root root 16433  1月 19  2017 cups-browsed.conf
-rw-r--r-- 1 root root  2931  2月 23  2018 cups-files.conf
-rw-r--r-- 1 root root  4629  7月  4 12:58 cupsd.conf
drwxr-xr-x 2 root root  4096  2月 23  2018 interfaces
drwxr-xr-x 2 root lp    4096 11月 29 11:35 ppd
-rw------- 1 root lp    2590 11月 29 11:37 printers.conf
-rw------- 1 root lp    2995 11月 29 11:36 printers.conf.O
-rw-r--r-- 1 root root   240  7月  4 13:00 raw.convs
-rw-r--r-- 1 root root   211  7月  4 13:00 raw.types
-rw-r--r-- 1 root root   142  2月 23  2018 snmp.conf
drwx------ 2 root lp    4096  7月  4 12:58 ssl
-rw-r----- 1 root lp     390 11月 29 22:54 subscriptions.conf
-rw-r----- 1 root lp     390 11月 29 22:08 subscriptions.conf.O

その中は

-rw-r----- 1 root lp 127373 11月 29 10:57 Canon_MP493_series.ppd
-rw-r----- 1 root lp 127347 11月 18 17:44 Canon_MP493_series.ppd.O
-rw-r----- 1 root lp  51269  7月 14 08:56 EPSON_EPSON_PX-1700F.ppd
-rw-r----- 1 root lp  51269  7月 10 23:36 EPSON_EPSON_PX-1700F.ppd.O

これをコピーして増やしても認識されない。その方法を探るうち新規プリンタの追加でppdファイルを指定すれば良いことを思いついた。つまり、ppdファイルを適当な所にコピーしておき、追加時にプリンタ名を適当な名前にしてからドライバ設定でそのppdファイルを指定すると/etc/cups/ppdにコピーされる。続けて言われるままにデフォルトを設定して用紙と印字品質などを変更する。

設定が終わると現在値が示される

というわけで普通のプリンタと、長形3号用、A4用、ハガキ用の4つができた。

Canon_MP493_series
Canon_MP493_series (Idle, Accepting Jobs, Not Shared)
Description:	Canon MP493 series
Location:	
Driver:	Canon PIXUS MP493 - CUPS+Gutenprint v5.2.11 (color)
Connection:	usb://Canon/MP493%20series?serial=C0A92F&interface=1
Defaults:	job-sheets=none, none media=iso_a4_210x297mm sides=one-sided
Canon_MP493_chou3
Canon_MP493_chou3 (Idle, Accepting Jobs, Not Shared)
Description:	Canon MP493 chou3ppd
Location:	
Driver:	Canon PIXUS MP493 - CUPS+Gutenprint v5.2.11 (color)
Connection:	usb://Canon/MP493%20series?serial=C0A92F&interface=1
Defaults:	job-sheets=none, none media=om_w340h666_119.94x234.95mm sides=one-sided
Canon_MP493_A4
Canon_MP493_A4 (Idle, Accepting Jobs, Not Shared)
Description:	Canon MP493 A4ppd
Location:	
Driver:	Canon PIXUS MP493 - CUPS+Gutenprint v5.2.11 (color)
Connection:	usb://Canon/MP493%20series?serial=C0A92F&interface=1
Defaults:	job-sheets=none, none media=iso_a4_210x297mm sides=one-sided
Canon_MP493_hagaki
Canon_MP493_hagaki (Idle, Accepting Jobs, Not Shared)
Description:	Canon MP493 hagaki
Location:	
Driver:	Canon PIXUS MP493 - CUPS+Gutenprint v5.2.11 (color)
Connection:	usb://Canon/MP493%20series?serial=C0A92F&interface=1
Defaults:	job-sheets=none, none media=jpn_hagaki_100x148mm sides=one-sided

PX-1700Fと合わせて5つになる。

使ってみる

A4用が[0]なのでこれが最初に出る。

用紙設定はchou3なのだが、このMediaSizeNameを指定できないのでデフォルトのA4になっている。マージンの左と上は指定通りで指定の幅と高さをA4から引いたものが右と下なのだろう

用紙はそのままにして、プリンタをchou3にしたところ、うまく印刷できた。用紙設定も変えるとマージンも変えなければ印刷範囲が異常に狭くなってしまう。目視で変更を確認できないのは不満ではあるが、用紙設定を手動で変えなくてもいいのであれば、これはこれでも容認できる。

ちなみにプリンタをchou3にしなくてもうまく行くこともある。これはA4のままマージンを多く調整しているので、A4のまま印刷してもちょうどよいということらしい。ただし、A4の左側に印刷されるので、小さな紙はセンターに配置するMP493ではまずい。ハガキは齟齬が生ずるのでだめ。

印刷の手順1

(1) DocFlavor と PrintRequestAttributeSet のインスタンスを引数にプリンタを探す。

PrintService[] psvs = PrintServiceLookup.lookupPrintServices(flavor,prset);

デフォルトの用紙で選択するのではなく対応可能な用紙での選択なのですべてが選択される。A3など明らかに対応不可な条件では候補に入らないので、条件は伝わっているようではある。

(2) 指定した用紙をデフォルトにするプリンタサービスを自動で特定する

デフォルトの用紙を知る方法や、用紙の名前の統一など不確実なところが多いので、プリンタ名に加えた文字列で識別してサービスから一つを選び出す。

int cdsv = 0;
for (int i=0;psvs.length>i;i++){
    if(psvs[i].getName().contains(paperkey)) cdsv= i;
}

paperkeyは文字列で、"A4","hagaki","chou3" のうちのどれか。

これで、psvs[cdsv]が用紙が合致したプリンタということになる。

(3) ダイアログを表示する。

本来は合致したプリンタ候補からプリンタを選ぶためのものらしい。DocFlavor と PrintRequestAttributeSet のインスタンスを再び指定しているのは冗長な気もする。

400,400は表示する座標の指定。画面中央や相対指定はない。

PrintService service = ServiceUI.printDialog(null, 400, 400, psvs, psvs[cdsv], flavor, prset);

(4) 印刷する

サービスからジョブを作り、SimpleDocをPrintableから作って、3度PrintRequestAttributeSetのインスタンスを添えて印刷する。実際にはtry-catchが必要。

3度なのは、指定を反映したいということから、しつこく要求した結果である。どうしてもだめということで、デフォルトの異なるプリンタサービスを用意したのであって、そうであるならば3度もいらないかもしれない。

DocPrintJob job = service.createPrintJob();
SimpleDoc doc = new SimpleDoc(printable, flavor, null);
job.print(doc, prset);

PrintServiceAttributeSetは

AttributsfromService : 6
0
Canon MP493 A4ppd
Canon_MP493_A4
accepting-jobs
supported
not-attempted

とプリンタの状態などのことで、用紙には関わっていない

印刷の手順1のプログラム

    public void doDialogCheck(String paperkey){
        DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
        PrintRequestAttributeSet prset = new HashPrintRequestAttributeSet();
        MediaSize ms = MediaSize.ISO.A4;
        if(paperkey.equals("hagaki")) ms = MediaSize.Other.JAPANESE_POSTCARD;
        else if (paperkey.equals("chou3")) ms = MediaSize.JIS.CHOU_3;
        float mw = ms.getX(MediaPrintableArea.MM);
        float mh = ms.getY(MediaPrintableArea.MM);
        float leftmm = 5.1f;
        float rightmm = 4.9f;
        float topmm = 4.8f;  //landscape前のTOP
        float bottomm = 5.2f;
        prset.add(new MediaPrintableArea(
           leftmm, topmm,
           (mw - leftmm - rightmm),
           (mh - topmm - bottomm), MediaPrintableArea.MM));
        prset.add(OrientationRequested.PORTRAIT);
        MediaSizeName mssname = MediaSize.findMedia(mw,mh,MediaPrintableArea.MM);
        if (mssname!=null) prset.add(mssname);
        System.out.println("(A)"+mssname);
        System.out.println("(B)"+ms.getMediaSizeName());
        System.out.println("BeforeLookup : "+prset.size());//test
        Attribute[] atbs= prset.toArray();
        for (int i=0;prset.size()>i;i++){
            System.out.println(atbs[i]);
        }
        PrintService[] psvs = PrintServiceLookup.lookupPrintServices(flavor,prset);
        PrintService   dpsv = PrintServiceLookup.lookupDefaultPrintService();
        int cdsv = 0;
        for (int i=0;psvs.length>i;i++){
            if(psvs[i].getName().contains(paperkey)) cdsv= i;
            System.out.println("sv("+i+") "+psvs[i].getName());
        }
        PrintServiceAttributeSet svset = psvs[cdsv].getAttributes();
        System.out.println("AttributsfromService : "+svset.size());//test
        atbs= svset.toArray();
        for (int i=0;svset.size()>i;i++){
            System.out.println(atbs[i]);
        }
        PrintService service = ServiceUI.printDialog(null, 400, 400, 
        psvs, psvs[cdsv], flavor, prset);

        System.out.println("AfterSelectService : "+prset.size());//test
        atbs= prset.toArray();
        for (int i=0;prset.size()>i;i++){
            System.out.println(atbs[i]);
        }
        //PrintQuality pq = PrintQuality.NORMAL;
        //prset.add(pq);

        if (service != null) {
            DocPrintJob job = service.createPrintJob();//++
            SimpleDoc doc = new SimpleDoc(new SomethingPrintable01(), flavor, null);
            try { 
                job.print(doc, prset); //psv
            }catch (PrintException e){ //psv
                System.err.println(e.getMessage());
            }
        }
    }

実行結果 A4

adachi@debian64:/media/adachi/S1T/java/prtest$ java TestX
(A)iso-a4
(B)iso-a4
BeforeLookup : 3
(5.1,4.8)->(200.0,287.0)mm
portrait
iso-a4
sv(0) Canon_MP493_A4
sv(1) Canon_MP493_chou3
sv(2) Canon_MP493_hagaki
sv(3) Canon_MP493_series
sv(4) EPSON_EPSON_PX-1700F
AttributsfromService : 6
0
Canon MP493 A4ppd
Canon_MP493_A4
accepting-jobs
supported
not-attempted
AfterSelectService : 4
(5.1,4.8)->(200.0,287.0)mm
portrait
1
iso-a4

実行結果 葉書

adachi@debian64:/media/adachi/S1T/java/prtest$ java TestX
(A)japanese-postcard
(B)japanese-postcard
BeforeLookup : 3
(5.1,4.8)->(90.0,138.0)mm
portrait
japanese-postcard
sv(0) Canon_MP493_A4
sv(1) Canon_MP493_chou3
sv(2) Canon_MP493_hagaki
sv(3) Canon_MP493_series
sv(4) EPSON_EPSON_PX-1700F
AttributsfromService : 6
0
Canon MP493 hagaki
Canon_MP493_hagaki
accepting-jobs
supported
not-attempted
AfterSelectService : 4
(5.1,4.8)->(90.0,138.0)mm
portrait
1
japanese-postcard

実行結果 長3

adachi@debian64:/media/adachi/S1T/java/prtest$ java TestX
(A)null
(B)null
BeforeLookup : 2
(5.1,4.8)->(110.0,225.0)mm
portrait
sv(0) Canon_MP493_A4
sv(1) Canon_MP493_chou3
sv(2) Canon_MP493_hagaki
sv(3) Canon_MP493_series
sv(4) EPSON_EPSON_PX-1700F
AttributsfromService : 6
accepting-jobs
0
Canon_MP493_chou3
not-attempted
supported
Canon MP493 chou3ppd
AfterSelectService : 4
(5.1,4.8)->(110.0,225.0)mm
portrait
Japanese long envelope #3
1
http://www.cresc.co.jp/tech/java/jps/JPS.htm
https://www.eeb.co.jp/wordpress/?p=384

印刷の手順2

結局、用紙の指定がうまく行かなかったので、Service中心で考えていくメリットがなくなった。実はもっとクラスServiceUIFactoryまで考えるとできる可能性もあるのだが、今回はここまでにしておく。

すると、ServiceUI.printDialog()の位置指定やSimpleDocの利用の意味が薄れる。

そこで後半をDocPrintJobでなく、PrinterJobに戻すことを考えてみる

(1)から(2)までは同じにしておく

(1) DocFlavor と PrintRequestAttributeSet のインスタンスを引数にプリンタを探す。

PrintService[] psvs = PrintServiceLookup.lookupPrintServices(flavor,prset);

デフォルトの用紙で選択するのではなく対応可能な用紙での選択なのですべてが選択される。A3など明らかに対応不可な条件では候補に入らないので、条件は伝わっているようではある。

DocFlavorなどを要求しない方法もある。今回はこれでもできる。

PrintService[] psvs = PrinterJob.lookupPrintServices();

(2) 指定した用紙をデフォルトにするプリンタサービスを自動で特定する

デフォルトの用紙を知る方法や、用紙の名前の統一など不確実なところが多いので、プリンタ名に加えた文字列で識別してサービスから一つを選び出す。

int cdsv = 0;
for (int i=0;psvs.length>i;i++){
    if(psvs[i].getName().contains(paperkey)) cdsv= i;
}

paperkeyは文字列で、"A4","hagaki","chou3" のうちのどれか。

これで、psvs[cdsv]が用紙が合致したプリンタということになる。

(3) ダイアログを表示する。

PrinterJob.getPrinterJob()でまず、何もないところからPrinterJobを取得し、それにsetPrintService()で(2)で選んだサービスを結びつける。

PrinterJob#printDialog()でダイアログを出す。ここからtry-catchが必要

pj.setPrintService(psvs[cdsv]);
pj.setPrintable(new SomethingPrintable01());
if (pj.printDialog(prset)) {...

(4) 印刷する

SimpleDocもいらず、単純になる。

pj.print( prset );

印刷の手順2のプログラム

途中の確認のための出力を削ったので見通しが良くなっている。

    public void doDialogCheck2(String paperkey){
        DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;

        PrintRequestAttributeSet prset = new HashPrintRequestAttributeSet();
        MediaSize ms = MediaSize.ISO.A4;
        if(paperkey.equals("hagaki")) ms = MediaSize.Other.JAPANESE_POSTCARD;
        else if (paperkey.equals("chou3")) ms = MediaSize.JIS.CHOU_3;
        float mw = ms.getX(MediaPrintableArea.MM);
        float mh = ms.getY(MediaPrintableArea.MM);
        float leftmm = 5.1f;
        float rightmm = 4.9f;
        float topmm = 4.8f;  //landscape前のTOP
        float bottomm = 9.2f;
        prset.add(new MediaPrintableArea(
           leftmm, topmm,
           (mw - leftmm - rightmm),
           (mh - topmm - bottomm), MediaPrintableArea.MM));
        prset.add(OrientationRequested.PORTRAIT);
        MediaSizeName mssname = MediaSize.findMedia(mw,mh,MediaPrintableArea.MM);
        if (mssname!=null) prset.add(mssname);
        PrintService[] psvs = PrintServiceLookup.lookupPrintServices(flavor,prset);
        //PrintService[] services = PrinterJob.lookupPrintServices();
        if (psvs.length > 0) {
            int cdsv = 0;
            for (int i=0;psvs.length>i;i++){
                if(psvs[i].getName().contains(paperkey)) cdsv= i;
            }
            PrinterJob pj = PrinterJob.getPrinterJob();
            try {
                pj.setPrintService(psvs[cdsv]);
                pj.setPrintable(new SomethingPrintable01());
                if (pj.printDialog(prset)) {
                    pj.print( prset );
                    //job.print(doc, prset);
                    }
            }catch (PrinterException e){  //PrintException e){
                System.err.println(e.getMessage());
            } //try-catch
        }else{ //if service found
            System.out.println("service not found");
        } //if service found
    }

用紙の目途がついて印字品質も試してみた。解像度は調べるのが面倒なので自動とし、Qualityでできないかとやってみたが、NORMAL を HIGH にするとプリンタサービスが見つからなくなる。

PrintQuality pq = PrintQuality.HIGH; //NORMAL;
prset.add(pq);

ダイアログでも常に灰色になって選択できない。

ブラウザでのCUPSの設定ではQualityはNORMALとMANUAL。設定を変えても変わった様子がない。用紙を plain paper を inkjet hagaki にするなどすると印字時間が長くなって切り替わったことがわかる。Java側からはできていない。

printableなクラス

上記で使ったprintableなクラス。印刷内容はこちらで決まる

前に作ったものの流用。今回はmainを使っていない。

SomethingPrintable01.java

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.print.PrinterJob;
import java.awt.print.Printable;
import java.awt.print.PageFormat;
import java.awt.print.PrinterException;
import java.awt.BasicStroke;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;

public class SomethingPrintable01 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("文字を書きます", 72, 120); //文字の印字
        g2.drawLine(72,140,288,160);           //線の描画
        double x = pf.getImageableX();
        double y = pf.getImageableY();
        double w = pf.getImageableWidth();
        double h = pf.getImageableHeight();
        Rectangle2D.Double rectg = new Rectangle2D.Double(x, y, w, h-y);
        g2.draw(rectg);
        double pt2mm = 25.4d/72;
        int x2 = (int)x*2;
        g2.drawString(String.format("x:%7.2fmm y  :%7.2fmm",x*pt2mm,y*pt2mm),x2,240);
        g2.drawString(String.format("w:%7.2fmm h-y:%7.2fmm",w*pt2mm,(h-y)*pt2mm),x2,300);

        return PAGE_EXISTS;
    }

    public static void main(String[] args) {
        PrinterJob pj = PrinterJob.getPrinterJob();
        pj.setPrintable(new SomethingPrintable01());
        if (pj.printDialog()) {
            try { pj.print(); }
            catch (PrinterException e) {
                System.out.println(e);
            }
        }
    }
}

印刷結果

プリンタは Canon MP493 後部の手差しトレイから紙を入れる。最大はA4。小さい紙は中央に寄せてセットするタイプ。以下A4,長3,葉書で印刷したが、印刷範囲の比較のため、すべてA4用紙に出力した。