Javaで帳票印刷 用紙の指定

PageFormatのメソッド

用紙の大きさや印字領域の大きさを返してくれる。今の所、用紙を指定していないので、デフォルトの値が返ってきます。

昔はUSレターなどがデフォルトの用紙でしたが、今回改めて確かめると最近A4になっていました。もっとも、ドライバ設定の問題もあるかもしれません。

java.awt.print.PageFormat

double 	getWidth()	用紙の幅(単位は1/72インチ。つまりはポイント)
double 	getImageableX()	描画が開始される一番左の座標。用紙の右の縁が0になるので左余白の幅に等しい。
                        	Imageableは画像を展開できるという意味か。
double 	getImageableWidth()	印字可能域の幅。
double 	getHeight()	用紙の高さ
double 	getImageableY()	描画が開始される一番上の座標。用紙の上の縁が0になるので上余白の幅に等しい。
double 	getImageableHeight()	印字可能域の高さ。
int 	getOrientation()	描画の向き。用紙は常に縦長に考える
void 	setOrientation(int orientation)	描画の向きを指定する。スタティックな定数フィールド値が定義されている
各値の位置関係(x方向のみ)
←─────────── Width ──────────→
 ImageableX→ ←─── ImageableWidth ───→   
        

用紙に合わせて拡大縮小しようとか、センタリングしようとかしなければ、気にする必要はありません。

帳票は大きさが決められていて、異なる大きさの紙に書こうとすることはまずないからです。Imageableな範囲を外れても無視(クリップ)されるだけなのでほとんど問題になりません(印刷が切れるというのは問題ですが)。

この余白はプリンタの制約ではなく見栄えのために適当に決められたものですが、プリンタに問い合わせるサービスを使えるなら別の使い道があります。しかし用紙の全面を使えるように努力してきた結果、これもあまり気にしなくても良くなってきています。

用紙サイズを確認するプログラム (PageSize.java)

Imageableな部分を線で囲むプログラムです。中央部にgetWidth()やgetImageableWidth()で得られる数値と、mmへの換算を印字します。

そのあとで System.out.println() で端末にも数値を書き出します。Printableなクラスのインスタンスのprint()メソッドの働きが少しわかってきます。

PageSize.java

package print01;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Font;
import static java.awt.Font.*;
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 PageSize implements Printable {
    @Override
    public int print(Graphics g, PageFormat pf, int pageIndex) {
        if (pageIndex != 0) return NO_SUCH_PAGE;
        Graphics2D g2 = (Graphics2D)g;

        g2.setFont(new Font(SANS_SERIF, PLAIN, 14));
        float strw ;
        BasicStroke stroke;
        strw=0.5f;
        stroke = new BasicStroke(strw);
        g2.setStroke(stroke);
        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);
        g2.draw(rectg);
        g2.drawString("x:"+x+"pt y:"+y+"pt w:"+w+"pt h:"+h+"pt",100,200);
        double pt2mm = 25.4d/72;
        g2.drawString("x:"+x*pt2mm+"mm y:"+y*pt2mm+"mm w:"+w*pt2mm+"mm h:"+h*pt2mm+"mm",100,240);
        double pw = pf.getWidth();
        double ph = pf.getHeight();
        g2.drawString("paper w:"+pw+"pt paper h:"+ph+"pt",100,280);
        g2.drawString("paper w:"+pw*pt2mm+"mm paper h:"+ph*pt2mm+"mm",100,320);

        System.out.println("pf.getWidth()="+pf.getWidth());
        System.out.println("pf.getImageableX()="+pf.getImageableX());
        System.out.println("pf.getImageableWidth()="+pf.getImageableWidth());
        System.out.println("pf.getHeight()="+pf.getHeight());
        System.out.println("pf.getImageableY()="+pf.getImageableY());
        System.out.println("pf.getImageableHeight()="+pf.getImageableHeight());
        System.out.println();

        return PAGE_EXISTS;
    }

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

実行結果 ImageableWidth, Height

水色(薄い青)で書かれた部分は実測値(mm)。手書きで書き加えています。

ImageableWidth,Heightを実測と比べる

端末の表示

System.out.println()の出力

印刷は一回でも、print()メソッドは2回呼び出されていることがわかります。数値の単位はポイントです。

$ java print01.PageSize
pf.getWidth()=595.0
pf.getImageableX()=72.0
pf.getImageableWidth()=468.0
pf.getHeight()=842.0
pf.getImageableY()=72.0
pf.getImageableHeight()=648.0

pf.getWidth()=595.0
pf.getImageableX()=72.0
pf.getImageableWidth()=468.0
pf.getHeight()=842.0
pf.getImageableY()=72.0
pf.getImageableHeight()=648.0

印刷物にも出ていますが、数値をmmにすると

Width 	209.9mm
ImageableX 	25.4mm
Imageablewidth 	165.1mm

Height 	297.0mm
ImageableY 	25.4mm
ImageableHeight 	228.6mm

A4の大きさは、

210mm ✕ 297mm

しかしprintDialog()で用紙サイズが伝わっていない

printDialog()の様子。用紙サイズはCustomになり、マージンも全部25.4mmになっています。上と左は25.4ですが他は異なりますから、上左の25.4が合っているのはどちらも切りよく1インチにしたということでしょう。。

printDialogの一般のページ。プリンタを選択できる printDialogのページ設定のページ。用紙サイズやマージンを変更できる

用紙の指定を PrintRequestAttributeSetで (RequestSize.java(省略あり))

プログラムはprint()メソッドに省略がありますが、SomethingPrintable.javaなどのprint()メソッドの内容と同じで良いからです。このプログラムは印刷内容に興味はありません。import部分はjavax.print.attribute...関係が増えているので注意

main()の記述は長く感じますが、いままでのものと基本構造は同じです。PrintRequestAttributeSetのインスタンスreqsetに3つの属性をaddして、printDialog()とprint()の引数として加えているだけです。

if(debug){...} の部分はAttributeSetの内容を書き出すためのもので、本来は必要がない部分です。

RequestSize.java(省略あり)

package print01;

import static java.awt.Font.*;
import java.awt.*;
import java.awt.print.*;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;

import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.HashPrintJobAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.PrintJobAttributeSet;
import javax.print.attribute.standard.MediaSize;
import javax.print.attribute.standard.MediaSizeName;
import javax.print.attribute.standard.OrientationRequested;
import javax.print.attribute.standard.MediaPrintableArea;
import javax.print.attribute.standard.MediaTray;
import javax.print.attribute.Attribute;

public class RequestSize implements Printable {
    @Override
    public int print(Graphics g, PageFormat pf, int pageIndex) {
         ......
         ......
        return PAGE_EXISTS;
    }
    public static void main(String[] args) {
        PrinterJob pj = PrinterJob.getPrinterJob();
        PrintRequestAttributeSet reqset = new HashPrintRequestAttributeSet();
        MediaSizeName medname = MediaSizeName.ISO_A4;
        reqset.add(medname);

        MediaSize medsize = MediaSize.getMediaSizeForName(medname);
        float medwidth  = medsize.getX(MediaPrintableArea.MM);
        float medheight = medsize.getY(MediaPrintableArea.MM);
        float topmm   = 8.8f;  //landscape前のTOP
        float bottomm = 7.7f;
        float leftmm  = 6.6f;
        float rightmm = 5.5f;
        reqset.add(new MediaPrintableArea(
           leftmm, topmm,
           (medwidth - leftmm - rightmm),
           (medheight - topmm - bottomm), MediaPrintableArea.MM));
        //reqset.add(OrientationRequested.REVERSE_LANDSCAPE);
        //reqset.add(OrientationRequested.LANDSCAPE);
        reqset.add(OrientationRequested.PORTRAIT);
        pj.setPrintable(new RequestSize());  //printable instance
        if (pj.printDialog( reqset )) {
            boolean debug = true;
            if(debug){
                Attribute[] attrs = reqset.toArray();
                for(int n=0 ; attrs.length > n ; n++){
                    System.out.println( attrs[n].getName()+":"+ attrs[n].toString()+";; ");
                }
            }
            try { pj.print( reqset ); }
            catch (PrinterException e) {
                System.err.println(e);
            }
        }
    }
}

実行結果(印刷ダイアログ)

MediaSizeName.ISO_A4; は メディアのサイズの A4(ISO/DIN & JIS) に

MediaPrintableArea の topmm=8.8f などは マージンの 上8.8mm に

OrientationRequested.PORTRAIT は 用紙の向き 縦 にそれぞれ伝えられています。

printDialogのページ設定のページ。

MediaSizeName を MediaSizeName.ISO_A3; にすると メディアのサイズは A3(ISO/DIN & JIS) になります。

printDialogのページ設定のページ。サイズがA3に変わった

また、A4のまま PORTRAIT を LANDSCAPE にする、つまり

//reqset.add(OrientationRequested.PORTRAIT);
reqset.add(OrientationRequested.LANDSCAPE);

とすると、用紙の向きが 横 になり、topmm=8.8f は右のマージンになります。

printDialogのページ設定のページ。マージンの位置が変わった

つまり、MediaPrintableAreaコンストラクタの第二引数のtopmmは、用紙を回転する前のtopで、用紙を右に90度回転させたかのように印刷するという事になります。

用紙を右に回転させたということは、印刷データを左に回転させるという事でもあります。プリンタの中では用紙の向きは固定ですから、この方が現実に近い受け止め方という事になります。インクジェットプリンタで縦の用紙が文書の頭から出てくるならば、左回転すれば文書の右側が先に出てくることになります。linuxではこのとおりになりますが、Windowsでは逆で文書の左側から出てきます。

AttributeSetの内容

1. A4の時

$ java print01.RequestSize
orientation-requested:portrait;;
copies:1;;
media:iso-a4;;
media-printable-area:(6.6,8.8)->(197.9,280.5)mm;;

media-printable-area:の2つめの括弧内は(ImageableWidth,ImageableHeight)です。A4ですから、

210-6.6-5.5=197.9
297-8.8-7.7=280.5

となり、計算は合っています

2. A3にプログラムを変更してコンパイルし直して実行

$ javac print01/RequestSize.java
$ java print01.RequestSize
orientation-requested:portrait;;
media-printable-area:(6.6,8.8)->(284.9,403.5)mm;;
copies:1;;
media:iso-a3;;

(ImageableWidth,ImageableHeight)も合わせて変化しています。A3ですから、

297-6.6-5.5=284.9
210*2-8.8-7.7=403.5

となり、やはり計算は合っています

3. AttributeSetの出力はダイアログの後なので、ダイアログで設定を変えるとそれが反映されます

給紙装置をトレイ2 にするとこうなります。

$ java print01.RequestSize
orientation-requested:portrait;;
copies:1;;
media:iso-a3;;
media-printable-area:(6.6,8.8)->(284.9,403.5)mm;;
sun-alternate-media:alternate-media: Paper Tray 2;;

Windowsの場合のAttributeSet

A3ポートレイト

はじめからセットしていない copies がでてこないだけで同じ。

Y:\>java print01.RequestSize
media:iso-a3;;
orientation-requested:portrait;;
media-printable-area:(6.6,8.8)->(284.9,403.5)mm;;

A4ランドスケープ

Y:\>java print01.RequestSize
media:iso-a4;;
orientation-requested:landscape;;
media-printable-area:(6.6,8.8)->(197.9,280.5)mm;;

ダイアログで給紙装置をトレイ2 ににした場合は異なります。

Y:\>java print01.RequestSize
media:用紙カセット2;;
orientation-requested:landscape;;
media-printable-area:(6.6,8.8)->(197.9,280.5)mm;;

給紙装置を指定すると media:iso-a4;;がなくなります。装置に入っている用紙を利用するのでこれはこれで正しいのです。media-printable-area:は変化しないので、印刷範囲は変わらないのかと思ったら、実際には用紙カセット2のA3の用紙にlandscapeで上下がセンタリングされた位置に出てきました。(6.6,8.8)の8.8が無視された形になっています。

Windowsでのスクリーンショット

printDialogのページ設定のページ。ソースが用紙カセット2になっている

実はlinuxで不具合

Debian GNU/Linux 7 (wheezy)で EPSON PX-1700F を使用していますが、トレイ1しか使えていないのです。そこで上記のトレイ2への変更を試しているわけです。Windowsでは自動選択でも、用紙カセット2の指定でもうまく行きます。

linuxではcupsでデフォルトをA3にしなければなりません。上記の出力で sun-alternate-media:alternate-media: あたりが苦しんでいる様子に見えます。