用紙サイズや余白を指定する方法の詳細
用紙サイズや余白など印刷方法に関する指示をJavaでは"attribute"、日本語では「属性」と呼びます。javax.print.attributeとjavax.print.attribute.standardの2つのパッケージを使います。standardのついたパッケージの中に用紙の大きさや印刷の向きなどの属性が定義されているので、この中から選んで"attributeset"にまとめて印刷時に指示するという手順になります。
上記のプログラム例では、次のような例を示しました。
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));//左余白,上余白,描画幅,描画高,単位
1行目でPrintRequestAttributeSetインターフェースを持つインスタンスをHashPrintRequestAttributeSetクラスを使用して作成しています。XxxxXxxxAttributeSetというクラスやインターフェースはいくつかありますが、とりあえずこれだけで済みます。このインスタンスにadd()メソッドを使って属性値を追加していきます。
最初に追加している印刷部数では、Copiesクラスのインスタンスを引数を3にして生成し、追加しています。もし、印刷部数を指定しなければデフォルトの値である「1部」が設定されます。どの属性も省略すればデフォルト値になりますから、必要な属性だけ追加すればよいのです。
次のMediaSizeNameでは、MediaSizeNameクラスのstaticなフィールドであるISO_A3を追加しています。フィールド値の型はMediaSizeNameです。
javax.print.attribute.standardパッケージの中に、CopiesやMediaSizeNameというクラスがあります。これらのクラスは「PrintRequestAttributeインターフェースを持っているので、PrintRequestAttributeSetに追加できる」という仕組みになっています。Copiesクラスなどを「属性カテゴリ」、インスタンスは「属性値」と呼ばれます。
javax.print.attribute.standardパッケージにはたくさんのクラスがありますが、よく使うと思われるものを拾っておきます。
以下rqsetはPrintRequestAttributeSetのインスタンスであるとします。
Copyes
印刷部数を指定します。
rqset.add(new Copies(5));
ページ数とは異なります。一度の印字で同じ出力が何部必要かということです。多くの場合1部なので省略します。
属性指定の例としてよく出てくるのは、わかりやすい例であって、プリンタ側でも必ず対応できるものだからだと思います。
newですからインスタンスで、引数の5という値が含まれている「もの」をaddする。ただの5だと何の数だかわからないけれども、Copiesクラスなので部数だと言っているわけです。PrintRequestAttributeSetの全体はSetだけれども、クラスがkeyでインスタンスがvalueのMapのSetという形です。
OrientationRequested
用紙上の印刷方向を指定します。取りうる値は4つですが、REVERSE_PORTRAITは指定できない(無視される)場合があります。
この値は、OrientationRequestedクラスのstaticなフィールド値で、値の型はOrientationRequestedです。
デフォルトはPORTRAITです。
rqset.add(OrientationRequested.LANDSCAPE); rqset.add(OrientationRequested.PORTRAIT); rqset.add(OrientationRequested.REVERSE_LANDSCAPE); rqset.add(OrientationRequested.REVERSE_PORTRAIT);
WindowsではLANDSCAPEとREVERSE_LANDSCAPEが逆になります。
後で説明しますが、どちらも間違っているわけではありません。余白の位置などは同じで、プリンタから出てくる時にどちらから出てくるかだけの問題です。
MediaSizeName
用紙のサイズを指定します。ISO_A4とかISO_A3とかJAPANESE_POSTCARDなど、Java 8では73個の用紙サイズが登録されています。
これらは、MediaSizeNameクラスのstaticなフィールドで、型はMediaSizeNameです。
MediaSizeNameクラスは、Mediaのサブクラスです。Mediaクラスには他にMediaName、MediaTrayを加えて全部で3つのサブクラスがあります。MediaName、MediaTray、MediaSizeNameのどれかを使って Mediaという属性を指定するということになっています。
MadiaNameには4つのフィールドしか定義されていません。一つはMadiaName.ISO_A4_WHITEで、これはA4の白い紙です。MediaName.ISO_A4_TRANSPARENTはA4のOHPシートです。これにA4でなくレターサイズの用紙の白い紙とOHPシートが加わって4つです。さらにサブクラスを定義して自分で拡張するように作っているようですが、このまま使えるものではありません。
MediaTrayはプリンタの給紙トレイを指定するものです。MediaTrayにはMAIN、MANUAL、TOP、MIDDLE、BOTTOMなど8つの値が定義されています。トレイと用紙のサイズを両方を指定する方法は用意されていません。
トレイと用紙の両方を指定しても、矛盾することがあるでしょうから、トレイを指定してプリンタにトレイ内の用紙の大きさを問い合わせるという手順を踏むか、トレイの用紙に合わせて拡大縮小して印刷するという使い方になるでしょう。帳票印刷では用紙の大きさが決まっていますから、結局MediaSizeNameだけを使って指定するということになるでしょう。
いくつか例を挙げると、
rqset.add(MediaSizeName.ISO_A3); //A3用紙 rqset.add(MediaSizeName.ISO_A4); //A4用紙 rqset.add(MediaSizeName.JIS_B5); //JIS B5(ISO_B5とは異なる) rqset.add(MediaSizeName.JAPANESE_POSTCARD); //葉書 rqset.add(MediaSizeName.JAPANESE_DOUBLE_POSTCARD); //往復はがき
この他に、ISOのA0~A10、B0~B10、JISのB0~B10、北米規格の各種封筒サイズなどがあります。
それぞれの用紙の実際のサイズはMediaSizeNameクラスではなく、MediaSizeクラスに格納されています。
MediaSizeというクラスは直接MediaSizeはAttributeSetに加えることができません。加えられたMediaSizeNameインスタンスからMediaSizeクラスを使って用紙の縦横の長さを得るという手順を踏んでいます。
MediaSizeクラスにはMediaSizeNameからMediaSizeを得るメソッドと、縦横の長さからMediaSizeNameを検索するためのメソッド、用紙の縦横の長さを得るメソッドが用意されています。後で説明するMediaPrintableAreaクラスで余白を指定する時に、これを使って用紙の大きさを取得します。
MediaSizeName msname = MediaSizeName.ISO_A4; rqset.add(msname); MediaSize msize = MediaSize.getMediaSizeForName(msname); float mwidth = msize.getX(MediaPrintableArea.MM); float mheight = msize.getY(MediaPrintableArea.MM);
MediaPrintableArea.MM はMediaPrintableAreaクラスの定数フィールドで、内実は整数1000です。MediaPrintableArea内で長さはμm単位の整数として格納されるので、MediaPrintableArea.MMの値で割り算をしてmm単位のfloatにする仕組みでしょう。MediaPrintableArea.INCHの値は25400ですから割り算するとインチになります。
MediaPrintableArea
余白を指定するために使います。
Javaのデフォルトの余白が上下左右それぞれ1インチ(25.4mm)なので、この範囲に印字しないのなら指定する必要はありません。
プリンターには印刷機構や紙送りなどハードウェア上の都合で用紙の全体に印刷できないことが普通です。写真で「縁無し」という方法もありますから、これも考慮すると事情はより複雑になります。
ハードウェア上の都合ですからプリンタに問い合わせるのが筋です。API仕様にも、PrintService.getSupportedAttributeValues()を参照せよとあります。しかし、一般的な帳票では用紙を隅々まで使うことは稀ですから、問い合わせずに5mmとか10mmあたりの余白を指定しても制限に引っかかることはないと考えられます。もし引っかかっても、その分は印刷されないだけでエラーになりませんし、レイアウトが乱れたりもしません。
直接A4縦置きの用紙に左,右,上,下に10.1,10.2,10.3,10.4(単位はmm)の余白を指定するならば次のようにします。数値を微妙に変えているのは0.1mm単位で指定できるということをアピールと、左,右,上,下がきちんと伝わることの確認のためです。
rqset.add(new MediaPrintableArea( 10.1f, 10.3f, 189.7f, 276.3f MediaPrintableArea.MM));//左余白,上余白,描画幅,描画高,単位
A4の横は210.0mmなので、210 - 10.1 - 10.2 = 189.7と計算して、10.1と189.7を指定します。図では次のようになります。
縦は297.0mmなので、297 - 10.3 - 10.4 = 276.3と計算して、10.3と276.3を指定します。図は省略します。
実際のプログラムでは、用紙のサイズをMediaSizeから得て、余白をリテラルで書いて描画可能域のサイズはそこから算出するのがいいと思います。L,R,T,Bはそれぞれ左,右,上,下の余白です。
MediaSizeName msname = MediaSizeName.ISO_A4;
rqset.add(msname);
MediaSize msize = MediaSize.getMediaSizeForName(msname);
float mwidth = msize.getX(MediaPrintableArea.MM);
float mheight = msize.getY(MediaPrintableArea.MM);
float L = 10.1f;
float R = 10.2f;
float T = 10.3f;
float B = 10.4f;
rqset.add(new MediaPrintableArea(
L, T, (mwidth - L - R), (mheight - T - B),
MediaPrintableArea.MM));
