SHOEISHA iD

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

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

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

宛名印刷と印刷プレビュー

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

  • このエントリーをはてなブックマークに追加

 印刷前のプレビューと宛名印刷を扱います。プレビューは仕組みを簡易版で確認した後、画面を拡大・縮小したり、印刷位置の微調整をしたり、複数のページの印刷の様子を確認し一括で印刷に進むような機能を持ったクラスを作ります。宛名印刷では住所をスペースや「丁目」「大字」「番地」など特定の文字列を区切り候補として使って、自動で改行して指定幅に収めるクラスを作ります。

  • このエントリーをはてなブックマークに追加

はじめに

 住所を印刷する場合を考えます。AdjustString.java(第4回を参照)で指定範囲に書ききれない場合には、次の行に書くことができましたが、住所の場合は例えば203という数字が2と03との間で区切られてはまずいわけです。ここで丁目とか番地とかで自動的に区切ることができないかということを考えてみます。世間的には住所をあらかじめ2つから3つの部分に分けて登録するというのが定番のようではありますが、あえて挑戦してみます。今回は横書きのみを扱います。年賀状によくある縦書きは次回にまわします。

 先に印刷プレビューをできるようにします。うまく区切られるかが印刷しなくとも画面で確認できるのは便利です。

住所とプレビュー
住所とプレビュー

横書き住所とその簡易プレビュー

 まず、住所は1人分のみを仮に入れて試作します。

 住所は横書きとして既出のAdjustString.javaを使って書き切れるまで改行していきます。

 プレビューは、printableなインターフェースを持たせて作ったクラスを、さらにJPanelを継承したものとして、自分自身にも描画します。具体的にはpaintComponent()メソッドを加えて、印刷に使うprint()メソッドに書いていた描画内容を独立したメソッドにして、paintComponent()とprint()の両方から呼び出すようにします。

 JPanelの表示には取りあえずJOptionPane.showOptionDialog()を使って簡単に済ませます。これは後で改良します。

PrintableWithPreview01.java
/** PrintableWithPreview01.java 
 AdjustString.javaを使い、1行に入り切らない時は2行に分けます。
 プレビューをJPanelとJOptionPane.showOptionDialogで実現します。
 どちらも仮の仕様で、すぐに改良する予定です。
 2019-11-06 Adachi Junichi
*/
import java.awt.*;
import java.awt.print.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;
import javax.swing.*;  //追加します

public class PrintableWithPreview01 extends JPanel implements Printable {//①
    String zip;
    String addr;
    String name;
    float mm2pt = 72/25.4f;
    public PrintableWithPreview01(String zip,String addr,String name){
        this.zip = zip;
        this.addr = addr;
        this.name = name;
        setBackground(Color.white);
    }
    public void setPanelSize(float w,float h){ //⑤
        Dimension d = new Dimension();
        d.setSize(w*mm2pt,h*mm2pt); 
        setPreferredSize(d);
    }
    @Override
    public void paintComponent(Graphics g){  //②
        super.paintComponent(g);
        drawPage(g);
    }
    @Override
    public int print(Graphics g, PageFormat pf, int pageIndex) {
        if (pageIndex != 0) return NO_SUCH_PAGE;
        drawPage(g);  //③
        return PAGE_EXISTS;
    }
    public void drawPage(Graphics g){  //④
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(Color.BLACK);
        //w:width ; v:virtical width ; pch:pitch
        float xzip1 = 44.0f;
        float xzip2 = 66.0f;
        float xpch1 = 13.9f/2;
        float xpch2 = 20.5f/3;
        float wzbox = 5.7f;
        float yzip  = 20.3f;
        float vzip  = 8.3f;
        g2.setFont(new Font("SansSerif", Font.PLAIN, 16));

        float padb = 1.5f; //yzipそのままだと下線に接するのでもどす値(mm)
        AdjustString ads;
        for(int i=0; 3>i; i++){
            ads = new AdjustString(g2, zip.substring(i,i+1),wzbox);
            ads.drawCenter(xzip1 + xpch1*i, yzip - padb);
        }
        for(int i=0; 4>i; i++){
            ads = new AdjustString(g2, zip.substring(i+4,i+1+4),wzbox);
            ads.drawCenter(xzip2 + xpch2*i, yzip - padb);
        }
        //float charh = g2.getFontMetrics().getFont().getSize()/mm2pt;
        float xaddr = xzip1-2.0f;
        float yaddr = yzip +vzip*1.5f;
        float ypch  = vzip;
        float waddr = xpch1*3+xpch2*4;
        g2.setFont(new Font("SansSerif", Font.PLAIN, 14));
        ads = new AdjustString(g2,addr,waddr);
        ads.drawLeft(xaddr, yaddr);
        int ct = 0;
        while(ads.hasNext()){
            ads = new AdjustString(g2, addr ,waddr, ads.getNextPt());
            ads.drawRight(xaddr, yaddr + ypch*(++ct));
        }
        g2.setFont(new Font("SansSerif", Font.PLAIN, 16));
        ads =  new AdjustString(g2,name+"様",waddr);
        ads.drawKintou(xaddr, yaddr + ypch*(++ct + 0.5f));
    }

    public static void main(String[] args) {
        String zip = "036-1234";
        String addr = "神奈川県相模原市南区南台6-8-10 三上ハイツ304";
        String name = "齋藤梨花";
        PrintableWithPreview01 pable = new PrintableWithPreview01(zip,addr,name);
        PrintRequestAttributeSet rqset = new HashPrintRequestAttributeSet();
        //MediaSizeName msname = MediaSizeName.ISO_A4; //A4用紙
        MediaSizeName msname = MediaSizeName.JAPANESE_POSTCARD;
        rqset.add(msname);
        MediaSize msize = MediaSize.getMediaSizeForName(msname);
        float mwidth  = msize.getX(MediaPrintableArea.MM);
        float mheight = msize.getY(MediaPrintableArea.MM);
        float L = 5.1f;
        float R = 5.2f;
        float T = 5.3f;
        float B = 5.4f;
        rqset.add(new MediaPrintableArea(
            L, T, (mwidth - L - R), (mheight - T - B),
            MediaPrintableArea.MM));
        pable.setPanelSize(mwidth,mheight);
        String[] goOrNot = {"印刷","中止"};
        int rev = JOptionPane.showOptionDialog( //⑥
                null, pable,
                "印刷プレビュー",
                JOptionPane.OK_CANCEL_OPTION,
                JOptionPane.PLAIN_MESSAGE,
                null,goOrNot,goOrNot[0]
        );
        //rev:"印刷":0,"中止":1
        if (rev!=0) System.exit(0);

        PrinterJob pj = PrinterJob.getPrinterJob();
        pj.setPrintable(pable);
        if (pj.printDialog(rqset)) {
            try { pj.print(rqset); }
            catch (PrinterException e) {
                System.err.println(e);
            }
        }
    }
}

 このプログラムのプレビューです。

住所とプレビュー
住所とプレビュー

プレビューの追加部分の詳しい解説

 丸付き数字はプログラム中の部分を指します。

① extends JPanelを加えます

 コンストラクタはデータを受け取る都合だけです。プレビューには関わりません。

② paintComponent()メソッドを加えます

 JPanelがもともと持っているものをオーバーライドして自身にプレビュー画面を描画します。このようにしておくとウィンドウの上下関係が変わったりしてJPanelの再描画が必要になった時に自動的に描き直されます。

 実際の描画は④のdrawPage()で行います。

③ print()メソッドも④のdrawPage()を呼び出すだけにします。

 印刷の時に呼び出されるのがprint()、プレビューで呼び出されるのがpaintComponent()で、両方から共通のdrawPage()を呼び出すことにより、描画内容を共通にできるというわけです。

④ そのdrawPage()です。

 描画の本体(均等割付、罫線....)です。これまでは、print()メソッドの中に直接書いたものをこちらに移動しました。

 paintComponent()からもprint()からも、Graphics gを引数に呼び出すようにし、このGraphicsに描画します。この仕組みのおかげで、画面表示用と印刷用で違いを気にする必要がなくなります。

 setRenderingHint()を加えています。アンチエイリアスの適用をして滑らかな描画をします。Windowsでは指定しなくてもある程度の平滑化がされていますが、Linuxでは必要です。

⑤ JPanelの大きさの希望値をセットします

 大きさをセットしないと、小さなダイアログが出るだけになってしまいます

 JPanelのサイズに関わりなく、drawPage()内では用紙上の位置をポイント(1/72インチ)単位で指示しています。用紙は葉書なので位置の最大値はポイントで表した葉書の大きさになります。葉書は100mm×148mmなので、これをポイントに換算してJPanelのサイズとして指定すれば過不足なく全体を表示できることになります。直接これをsetPreferredSize()で指定しても良いのですが、今回はmainで印刷のためにMediaSizeから用紙サイズを求めているので、それを使うことにしました。計算では283.46×419.52になりますが、284×420に切り上げられているようです。画面ではどう頑張ってもピクセルですから、整数であることは理にかなっています。A4なら210.0mm×297.0mmで、596×842になります。

⑥ 印刷の前にダイアログで表示します

 PrinterJobのインスタンスにPrintableなインスタンス(pable)を渡す前に、オプションダイアログを使って表示します。pableはJPanelを継承しているのでオプションダイアログを使って表示できます。

 このダイアログはJOptionPaneクラスにいくつか用意されているstaticなメソッドの一つですが、選択肢を"印刷"、"中止"にして分かりやすくするためshowOptionDialog()にしました。印刷を選択すると、これまでどおり、PrinterJobのインスタンスに Printableなインスタンス(pable)を渡して印刷のダイアログに進みます。

 拡大や縮小、スクロールなどはこれからの追加課題です。

会員登録無料すると、続きをお読みいただけます

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

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

メールバックナンバー

次のページ
住所の区切りを自動化する

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

  • このエントリーをはてなブックマークに追加
Javaの標準機能だけで実現する帳票印刷連載記事一覧

もっと読む

この記事の著者

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

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

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

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

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/11894 2020/07/08 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング