PrinterJobでプリンタ選択印刷
作成したPrinterSupportedクラスを使って、実際に印刷する方法を説明します。
最初に「PrinterJobでの印刷」の項目で紹介した方法は次のとおりでした。例として3部印刷を使うのはやめにして、横置きにしています。
Printable pable = new SomethingPrintable00();
//用紙サイズなどの指定をrqsetに集約
PrintRequestAttributeSet rqset = new HashPrintRequestAttributeSet();
rqset.add(OrientationRequested.LANDSCAPE);
rqset.add(MediaSizeName.ISO_A3); //A3用紙
rqset.add(new MediaPrintableArea(
10.1f, 10.3f, 276.7f, 399.3f,
MediaPrintableArea.MM));//左余白,上余白,描画幅,描画高,単位
//このrqsetを指定してダイアログ提示と印刷
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPrintable(pable);
if (pj.printDialog(rqset)) {
try { pj.print(rqset); }
catch (PrinterException e) {
System.err.println(e);
}
}
PrinterSupportedクラスを使うと、こうなります。
Printable pable = new SomethingPrintable00();
PrintRequestAttributeSet rqset = new HashPrintRequestAttributeSet();
rqset.add(OrientationRequested.LANDSCAPE);
PrinterSupported supp = new PrinterSupported();
float x=297f;
float y=420f; //A3
PrintService sv = supp.printerSetTo(x,y,rqset);
if (sv !=null) {
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPrintable(pable);
try {
pj.setPrintService(sv);
if (pj.printDialog(rqset)) pj.print(rqset);
}catch (PrinterException e) {
System.err.println(e);
}
}
用紙の幅と高さをmmで指定して、PrinterSupportedクラスのprinterSetTo()メソッドを呼び出します。MediaPrintableAreaによる余白の指定は、普通の帳票なら10mm程度でOKなのでprinterSetTo()にまかせてしまいます。
あまり知られていないのは、pj.setPrintService(sv)ではないでしょうか。PrinterJobのインスタンスにもあとからプリンタ指定ができます。
printDialog()を残してあります。プリンタ選択は終わっていますから、なくしても構いません。印刷の最終確認のつもりで置いています。
プリンタ選択+PrinterJobの印刷プログラム例
前項の方法を使って実際に印刷するプログラムを紹介しておきます。
もちろん、このプログラムの実行には、PrinterSupported.javaが必要です。
import java.awt.*;
import java.awt.geom.*;
import java.awt.print.*;
import static java.awt.Font.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;
import java.time.*;
import java.time.format.*;
/** SomethingPrintable02.java
PrintRequestAttributeSet と
PrinterSupportedクラスを使って選択したプリンタ(と用紙)と
PrinterJobを使う帳票印刷の典型例
起動時の引数で長形3号、葉書、角形2号、A3、A4、角形6号、洋形6号
の用紙に印刷できます。プログラム中のX,Yの値を変更すれば
他の用紙にも印刷可能です。
コメント行の変更で縦置き、横置きも変更できます
2019-06-03 adachi
*/
public class SomethingPrintable02 implements Printable {
@Override
public int print(Graphics g, PageFormat pf, int pageIndex) {
if (pageIndex != 0) return NO_SUCH_PAGE;
Graphics2D g2 = (Graphics2D)g;
float mm2pt = 72/25.4f;
float x0 = 0.0f*mm2pt;
float y0 = 10.5f*mm2pt;
float p = 1.0f*mm2pt;
float h3 = 3.0f*mm2pt;
float h5 = 5.0f*mm2pt;
float h7 = 7.0f*mm2pt;
float h10 = 10.0f*mm2pt;
g2.setStroke(new BasicStroke(0f));
Line2D line = new Line2D.Float();
line.setLine(x0, y0, x0+50.0f*mm2pt, y0);
g2.draw(line);
float x,ye;
for(int i=0;50>=i;i++){
x = x0 + p*i;
ye = (i%5==0) ?h5:h3;
ye = (i%10==0)?h7:ye;
line.setLine(x, y0, x, y0+ye);
g2.draw(line);
}//for
g2.setFont(new Font(SANS_SERIF, PLAIN, 9));
double ix = pf.getImageableX();
double iy = pf.getImageableY();
double iw = pf.getImageableWidth();
double ih = pf.getImageableHeight();
Rectangle2D.Double rectg = new Rectangle2D.Double(ix, iy, iw, ih);
g2.draw(rectg);
g2.setPaint(Color.RED);
rectg = new Rectangle2D.Double(ix+1, iy+1, iw-2, ih-2);
g2.draw(rectg);
g2.setPaint(Color.BLACK);
float yptch = 5.0f*mm2pt;
float xp = (float)ix + 2f*mm2pt;
float yp = y0+h5 + yptch;
int ct = 1;
double pt2mm = 25.4/72;
String sfmt = "%s:%5.1f %s:%5.1f";
double pwmm = pf.getWidth()*pt2mm;
double phmm = pf.getHeight()*pt2mm;
double ixmm = ix*pt2mm;
double iymm = iy*pt2mm;
double iwmm = iw*pt2mm;
double ihmm = ih*pt2mm;
String whx = String.format(sfmt,"W",pwmm,"H",phmm);
g2.drawString(whx, xp, yp+yptch*ct++);
whx = String.format(sfmt,"L",ixmm,"R",pwmm-ixmm-iwmm);
g2.drawString(whx, xp, yp+yptch*ct++);
whx = String.format(sfmt,"T",iymm,"B",phmm-iymm-ihmm);
g2.drawString(whx, xp, yp+yptch*ct++);
whx = String.format(sfmt,"w",iwmm,"h",ihmm);
g2.drawString(whx, xp, yp+yptch*ct++);
int ori = pf.getOrientation();
g2.drawString("0:LAND 1:PORT 2:RevL =[ "+ori+" ]", xp, yp+yptch*ct++);
String muki = "Unknown";
if (ori==PageFormat.LANDSCAPE) muki = "LANDSCAPE";
if (ori==PageFormat.PORTRAIT) muki = "PORTRAIT";
if (ori==PageFormat.REVERSE_LANDSCAPE) muki = "REVERSE_LANDSCAPE";
g2.drawString(muki , xp, yp+yptch*ct++);
g2.drawString(System.getProperty("os.name"), xp, yp+yptch*ct++);
LocalDateTime d = LocalDateTime.now();
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
g2.drawString(d.format(f), xp, yp+yptch*ct++);
x0 = 50.5f*mm2pt;
y0 = 25.5f*mm2pt;
g2.setColor(Color.RED);
line.setLine(x0, y0, x0+h5, y0);
g2.draw(line);
g2.setColor(Color.ORANGE);
line.setLine(x0, y0, x0, y0+h5);
g2.draw(line);
g2.setColor(Color.GREEN);
line.setLine(x0, y0, x0-h5, y0);
g2.draw(line);
g2.setColor(Color.BLUE);
line.setLine(x0, y0, x0, y0-h5);
g2.draw(line);
String sfmt2 = "交点は X:%5.1f Y:%5.1f";
whx = String.format(sfmt2,x0/mm2pt,y0/mm2pt);
g2.drawString(whx, x0-h5, y0+h7);
g2.setColor(Color.BLACK);
return PAGE_EXISTS;
}
public static void main(String[] args) {
boolean DEBUG = true;
int job = 4;
float x=210f;
float y=297f;
if (args.length==0) {
System.out.println("0:chou3 1:hagaki 2:kaku2 3:A3");
System.out.println("4:A4 5:kaku6 6:you6 ");
}
if (args.length>0) 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
Printable pable = new SomethingPrintable02();
PrintRequestAttributeSet rqset = new HashPrintRequestAttributeSet();
//rqset.add(OrientationRequested.LANDSCAPE);
//rqset.add(OrientationRequested.REVERSE_LANDSCAPE);
PrinterSupported supp = new PrinterSupported();
PrintService sv = supp.printerSetTo(x,y,rqset);
if (sv !=null) {
if(DEBUG){
System.out.println(">> printer:"+sv.getName());
Media med = (Media)rqset.get(Media.class);
System.out.print(">> media :"+med.toString());
OrientationRequested ori =
(OrientationRequested)rqset.get(OrientationRequested.class);
System.out.println((ori!=null)?" :"+ori.toString():" :PORTRAIT");
}
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPrintable(pable);
try {
pj.setPrintService(sv);
if (pj.printDialog(rqset)) pj.print(rqset);
}catch (PrinterException e) {
System.err.println(e);
}
}
}
}
実行結果
まず、Windows 10から葉書で印刷したものです。印刷内容はSomethingPrintable01.javaとOriTest.javaを混ぜたようなものになっています。葉書にしたのは掲載しやすいからです。
端末(PowerShellのウインドウ)にでる出力は次のようになります。if(DEBUG){...}部分のものです。
PS Z:\java\print02w> java SomethingPrintable02 1 >> printer:Canon MP493 series Printer >> media :japanese-postcard :PORTRAIT
GUIの流れを書いておきます。
PrinterSupportedクラスからのダイアログがでます。
選択して[OK]すると、もしもサイズに選択肢があれば用紙を選ぶ画面になります。1つしか選択肢がないとスキップします。ここで用紙サイズ、余白がPrintRequestAttributeSetにaddされます。このプログラムがプリンタと属性に責任を持つのはここまでです。
次にこのプリンタの他の属性が、SomethingPrintable02のmainからのダイアログで確認できます。
これは、pj.printDialog(rqset)で出しているものです。
「ページ設定」のタブも確認します。余白も伝わっています。
printDialog()になってからの変更も可能ですが、以下の注意を踏まえてください。
API仕様ドキュメントによると、プリンタや用紙サイズなどをダイアログ中で変更すると属性値がコピーされ、特定の属性値をサポートしないプリンタをユーザーがブラウズする場合、コピーされる新規値として、そのサービスのデフォルトが使用されるとなっています。つまり葉書をサポートしないプリンタに一度移ると、A4などに変更になってしまいます。[印刷]をクリックするタイミングでコピーが設定値として適用されるようです。
長形3号封筒への印刷
今回のPrinterSupportedクラスを使って長形3号封筒などへの印刷が可能になりました。
印刷見本
Linuxの場合はただ用紙サイズの指定が変わるだけですが、Windows+MP493プリンタの場合には次のような現象が生じます。
印刷開始直前に次のようなダイアログがでます。
つまり、この手の封筒は底の方を先にして入れるように指示されます。これは理にかなったことです。もし、頭から紙送りされた場合、封の蓋の部分は用紙の大きさには含まれませんから、正しい位置に印刷されません。ここを折った状態で印刷すればいいのですが、面倒な上に紙送りでトラブルの元になる可能性が大です。
Linuxではこれは自分で解決しなければならず、印刷するデータを180度回転させることで実現していました。Windowsで自動でやってくれるのは良いことではありますが、OSを判断して動作を変える必要があります。
一応、印刷するデータを180度回転させる例を示しておきます。次に示すのは、「SomethingPrintable02.java」 のprint()メソッドの変更です。 変更は現在のAffineTransformをoldに格納しておき、rotate()メソッドで回転、描画が終わったらoldを戻しておきます。このプログラムは1枚しか印刷しないので、oldに関わる2行は不要かもしれませんが、今後のために書いておきます。
public int print(Graphics g, PageFormat pf, int pageIndex) {
if (pageIndex != 0) return NO_SUCH_PAGE;
Graphics2D g2 = (Graphics2D)g;
AffineTransform old = g2.getTransform();
g2.rotate(Math.PI,pf.getWidth()/2,pf.getHeight()/2);
/*
ここで今までどおりの描画を行う
*/
g2.setTransform(old); //rotate
return PAGE_EXISTS;
}
