SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Javaの標準機能だけで実現する帳票印刷

均等割付・右寄せ・センタリングを簡単に扱う道具を作る

Javaの標準機能だけで実現する帳票印刷 第4回

文字列配置プログラムAdjustStringOvf.java

 まず、指定範囲に入りきらない場合も強行する方向で作ります。指定範囲が十分な幅を持つ場合には予想通りに振る舞います。

AdjustStringOvfの動作イメージ
AdjustStringOvfの動作イメージ

 指定範囲が十分な幅を持たない場合には、均等割付けは文字を重ねて印字し、その他は指定範囲からはみ出して印字します。これはこのようになることを望んだわけではなく、アルゴリズム上、自然にこうなります。文字列に対して常に十分な範囲を指定できるならば、このプログラムで十分です。また、均等割付けについてはこの方が都合の良い場合もあるかもしれません。

指定範囲が狭すぎる場合のAdjustStringOvfの動作イメージ
指定範囲が狭すぎる場合のAdjustStringOvfの動作イメージ

 プログラムです。

AdjustStringOvf.java
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.image.BufferedImage;
public class AdjustStringOvf {
    String str;
    int cpct;    //文字数
    float wdmm;  //width in mm
    float remm  = 0f;  //remain in mm;
    float gapmm = 0f;
    Graphics2D g2;
    FontMetrics fm;
    float mm2pt = 72/25.4f;
    float pt2mm = 25.4f/72;
    /** areawidth に str を入れる時の余り remm を計算 */
    public AdjustStringOvf(Graphics2D g2, String str, float areawidth) {
        this.g2 = g2;
        fm = g2.getFontMetrics();
        this.str = (str!=null) ? str : "";  //nullなら""
        wdmm = (areawidth>=0f) ? areawidth : 0f;   //負なら0
        cpct = str.codePointCount(0,str.length()); //strのcp数
        remm = wdmm - fm.stringWidth(str)*pt2mm;   //あまりを計算
        gapmm = remm/(cpct-1);
    }
    /** 文字列・余り・文字数 をレポートする。(デバッグ用) */
    public String report(){
        String rstr = String.format("str=%s /remm=%f /str.length()=%d /cpct=%d",
                                     str,    remm,    str.length(),    cpct);
        return rstr;
    }
    /** areawidth のなかに収まるかを答える */
    public boolean isFitInto(){
        return remm>=0;
    }
    /** 文字列の幅を引いたあまりをmmで返す */
    public float getRemain(){
        return remm;
    }
    /** 文字間の隙間をmmで返す */
    public float getGap(){
        return gapmm;
    }
    /** 文字列を左寄せで描画する */
    public void drawLeft(float hm, float vm) {
        g2.drawString(str,hm*mm2pt,vm*mm2pt);
    }
    /** 文字列を右寄せで描画する */
    public void drawRight(float hm, float vm) {
        g2.drawString(str,(hm+remm)*mm2pt,vm*mm2pt);
    }
    /** 文字列をセンタリングで描画する */
    public void drawCenter(float hm, float vm) {
        g2.drawString(str,(hm+remm/2)*mm2pt,vm*mm2pt);
    }
    /** 文字列を均等割付で描画する */
    public void drawKintou(float hm, float vm) {
        float hpp = 0;
        int strlen = str.length();
        if (cpct>1){
            int i=0;
            int nexti = 0;
            int ct = 0;
            while (strlen>i){ 
                nexti = str.offsetByCodePoints(i,1);
                g2.drawString(str.substring(i,nexti),(hm+gapmm*ct)*mm2pt+hpp,vm*mm2pt);
                hpp = fm.stringWidth(str.substring(0,nexti)); //pt
                i=nexti;
                ct++;
            }
        }else{
            drawCenter(hm, vm);
        }
    }
    public static void main( String[] args ) {
        float mm2pt = 72/25.4f;
        BufferedImage buffimg = new BufferedImage(800,600,BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = buffimg.createGraphics();
        float w = (args.length>0) ? Float.parseFloat(args[0]):50f;
        String str="東西【丈`】南北";
        System.out.println("文字列="+str+" /枠幅(mm)="+w);
        float x = 30.1f;
        float y = 30.2f;
        float h = 16.3f;
        AdjustStringOvf ads = new AdjustStringOvf(g2,str,w);
        System.out.println(ads.report());
        System.out.println(ads.getGap());
        ads.drawKintou(x,y+h);
    }
}

 mainメソッドは簡易な確認のために付けてあります。環境が許せば、【丈`】をサロゲートペアを使う本来の字に入れ替えることができますが、このままでも問題ありません。以下は実行結果です。

adachi@debian64:/media/adachi/S1T/java/print04$ java AdjustStringOvf
文字列=東西【丈`】南北 /枠幅(mm)=50.0
str=東西【丈`】南北 /remm=28.833334 /str.length()=6 /cpct=5
7.2083335

AdjustStringOvf.javaを使って帳票を印刷する例

 AdjustStringOvf.javaを使って文字を印字するプログラムです。

 A4の用紙に「東西【丈`】南北」を、前項の図「AdjustStringOvfの動作イメージ」のように印刷します。

 このプログラムも環境が許せば、【丈`】をサロゲートペアを使う本来の字に入れ替えて実行できます。画面への表示は行いませんので仮想端末がサロゲートペアに対応しなくても、印刷は可能です。

PrintableWithAdjustStringOvf.java
/** PrintableWithAdjustStringOvf.java 
 AdjustStringOvf.java を使うプログラム例。
 PrintRequestAttributeSet と PrinterJobを使い
 Service を使わない帳票印刷の典型例
 2019-09-25 adachi
*/
import java.awt.*;
import java.awt.print.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;

public class PrintableWithAdjustStringOvf 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(Font.SERIF, Font.PLAIN, 36));
        float mm2pt = 72/25.4f;
        String str="東西【丈`】南北";
        float x = 20.1f;
        float y = 20.2f;
        float w = 100f;
        float h = 16.3f;
        float padx = 3f;
        float pady = 2f;
        g2.setColor(Color.BLACK);
        AdjustStringOvf ads = new AdjustStringOvf(g2,str,w-padx*2);
        ads.drawKintou(x+padx,y+h-pady); //均等割付
        ads.drawLeft(x+padx,y+2*h-pady); //左寄せ
        ads.drawRight(x+padx,y+3*h-pady); //右寄せ
        ads.drawCenter(x+padx,y+4*h-pady); //センタリング
        return PAGE_EXISTS;
    }

    public static void main(String[] args) {
        Printable pable = new PrintableWithAdjustStringOvf();
        PrintRequestAttributeSet rqset = new HashPrintRequestAttributeSet();
        MediaSizeName msname = MediaSizeName.ISO_A4; //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));
        PrinterJob pj = PrinterJob.getPrinterJob();
        pj.setPrintable(pable);
        if (pj.printDialog(rqset)) {
            try { pj.print(rqset); }
            catch (PrinterException e) {
                System.err.println(e);
            }
        }
    }
}

 プログラムの核心部分は、次の部分です。文字列と印字領域の幅をmm単位で与えてインスタンスを作り、それに対して配置を指定します。

AdjustStringOvf ads = new AdjustStringOvf(g2,str,w-padx*2);
ads.drawKintou(x+padx,y+h-pady); //均等割付
ads.drawLeft(x+padx,y+2*h-pady); //左寄せ
ads.drawRight(x+padx,y+3*h-pady); //右寄せ
ads.drawCenter(x+padx,y+4*h-pady); //センタリング

 padxとpadyは余白、hは(x,y)が文字領域の左上を指していることを前提として下に移動するための量です。(x,y)が文字のベースラインの左端を指していることを前提とするならばもっと簡単にads.drawKintou(x,y)と書いてしまって構いません。

次のページ
文字列配置プログラムAdjustString.java

この記事は参考になりましたか?

Javaの標準機能だけで実現する帳票印刷連載記事一覧

もっと読む

この記事の著者

安達 順一(アダチ ジュンイチ)

私立高校に理科・情報の教員として勤めていました。Linuxサーバー/クライアントの授業システムを作り、移動プロファイルで運用していました。教員用にもLinuxサーバーを用意し成績処理プログラムを書きました。情報の学校設定科目ではウェブページ制作とjavaのプログラミングの初歩の授業を作りました。情報...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/12449 2020/06/29 11:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー