javaで帳票印刷 フォームの枠に文字を入れる

位置を指定して文字列を書く

これまでに調べたことから、紙の左辺からの距離と上辺からの距離を指定すれば、意図したところに文字列を印刷でき、文字の大きさや色も指定できることがわかりました。ただし位置は指定したポイントが先頭文字の左下の点になります。

また線も太さを指定して任意の2点を結ぶことができることもわかりました。

長さの単位はポイント(1/72インチ)で、小数(floatかdouble)での指定が可能なので解像度も十分です。

ポイントとmmの換算は次のとおりです。

72を25.4にするには72で割って25.4を掛ける

郵便番号を入れてみる

手始めに郵便番号を枠に入れてみます。枠は広いので適当でも何とかなりますが、後々のために精密に取り組んでみます。

まず、位置を測ります。定規は信頼のおけるものを使います。30cmの定規を2本合わせたら1mmの違いがあるということも経験しています。

郵便番号の枠の位置を測定

始めの3桁と後の4桁の間に1mm程度の余分な隙間がありますから、それぞれ開始位置を測ります。線の左側を測定しました。

float hbas1 = 44.0f;
float hbas2 = 66.0f;

長年やっているので名前の付け方がだいたい決まっています。水平方向の位置をh, 垂直方向の位置をvで始めます。幅を表すものはhw,vwで始めます。今考えるとなぜx,yでなかったのか不思議です。

マス目のピッチを測ります。273とあるマス目では2マスが13.9mmです。3つの四角の間に隙間があるので3つの幅を測って3で割っては正確ではありません。13.9÷2がピッチということになります。同様に0122の部分はマス目3つで20.5と測ります。

float hptch1 = 13.9f/2;
float hptch2 = 20.5f/3;

縦位置は共通で四角の下辺の上を測ります。四角の高さは8.3です。今回は内側を測っています。

float vbas  = 20.3f;
float vwrec = 8.3f;

mmをptに換算するために定数を定義しておきます。

float mm2pt  = 72/25.4f;

文字の大きさは経験上16ptの文字にします。

g2.setFont(new Font("SansSerif", Font.PLAIN, 16));

プログラムの骨子

String zip = "2345678"; //7桁の郵便番号
float hbas1 = 44.0f;
float hbas2 = 66.0f;
float hptch1 = 13.9f/2;
float hptch2 = 20.5f/3;
float vbas  = 20.3f;
float vwrec = 8.3f;
float mm2pt  = 72/25.4f;
g2.setFont(new Font("SansSerif", Font.PLAIN, 16));

float vbk = 1.5f; //vbasそのままだと下線に接するのでもどす値(mm)
float hfw = 2.0f; //vbasそのままだと左線に接するので進める値(mm)

for(int i=0; 3>i; i++){
    g2.drawString(zip.substring(i,i+1),(hbas1+hfw+hptch1*i)*mm2pt,(vbas-vbk)*mm2pt);
}
for(int i=0; 4>i; i++){
    g2.drawString(zip.substring(i+3,i+4),(hbas2+hfw+hptch2*i)*mm2pt,(vbas-vbk)*mm2pt);
}

mainメソッドでA4指定をしていたのを葉書にします。これらは定数フィールド値で用意されています。

//MediaSizeName medname = MediaSizeName.ISO_A4;
MediaSizeName medname = MediaSizeName.JAPANESE_POSTCARD;

実行結果

印刷をしてみて、印刷が上になる時は vbk の値を減らし、左による時は hfw を増やすなどの調整をして合わせて完成です。

枠の中の数字

プログラム全リスト (ZipWaku.java)

すぐに試せるように全文を出しておきますが、一つ前の RequestSize.java の print()メソッドの中を入れ替え、用紙とマージンを変更しただけです。

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

        String zip = "2345678"; //7桁の郵便番号
        float hbas1 = 44.0f;
        float hbas2 = 66.0f;
        float hptch1 = 13.9f/2;
        float hptch2 = 20.5f/3;
        float vbas  = 20.3f;
        float vwrec = 8.3f;
        float mm2pt  = 72/25.4f;
        g2.setFont(new Font("SansSerif", Font.PLAIN, 16));

        float vbk = 1.5f; //vbasそのままだと下線に接するのでもどす値(mm)
        float hfw = 2.0f; //vbasそのままだと左線に接するので進める値(mm)

        for(int i=0; 3>i; i++){
            g2.drawString(zip.substring(i,i+1),(hbas1+hfw+hptch1*i)*mm2pt,(vbas-vbk)*mm2pt);
        }
        for(int i=0; 4>i; i++){
            g2.drawString(zip.substring(i+3,i+4),(hbas2+hfw+hptch2*i)*mm2pt,(vbas-vbk)*mm2pt);
        }

        return PAGE_EXISTS;
    }

    public static void main(String[] args) {
        PrinterJob pj = PrinterJob.getPrinterJob();
        PrintRequestAttributeSet reqset = new HashPrintRequestAttributeSet();
        //MediaSizeName medname = MediaSizeName.ISO_A4;
        MediaSizeName medname = MediaSizeName.JAPANESE_POSTCARD;
        reqset.add(medname);

        MediaSize medsize = MediaSize.getMediaSizeForName(medname);
        float medwidth  = medsize.getX(MediaPrintableArea.MM);
        float medheight = medsize.getY(MediaPrintableArea.MM);
        float topmm   = 5.5f;
        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.PORTRAIT);
        pj.setPrintable(new ZipWaku());  //printable instance
        if (pj.printDialog( reqset )) {
            try { pj.print( reqset ); }
            catch (PrinterException e) {
                System.err.println(e);
            }
        }
    }
}

改めてPrintRequestAttributeSetの説明

PrintRequestAttributeSetのインスタンスreqsetに3つの属性をaddして、printDialog()とprint()の引数として加えているだけです。と説明しましたが、3つの属性がプリンタにどう伝わったか書き出す方に注意が行ってしまい肝心の内容の説明が中途半端でした。

PrintRequestAttributeSetのインスタンスは、new HashPrintRequestAttributeSet()で作ります。

3つの属性とは、名前、印刷可能域、向きです。

名前(MediaSizeName)にも、サイズ(MediaSize)にもA4というフィールド値が用意されていますが、用紙の変更は一箇所の変更で済ませたいものです。名前からサイズを取得できる(getMediaSizeForName(medname))ので名前を中心にします。サイズを取得したら、マージンを適当に決めて可能域(ImageableArea)の開始座標と幅と高さを計算させ、MediaPrintableAreaのインスタンスにして、印刷可能域の属性をセットします。このコンストラクタにはmm単位のものがあるので、ポイントに換算する必要がありません。

向きは3つから選択します。

プリンタから出てくる向きに合わせるとこうなります。Debian(linux)の場合。

Windowsの場合 LANDSCAPEとREVERSE_LANDSCAPEが入れ替わります。