SHOEISHA iD

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

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

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

指定幅に文章を書くための追い出し禁則処理

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

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

Student.java

 成績と出欠データを保持する生徒のクラス。今回のプログラムの目的は、帳票印刷とプレビューなのでクラスの設計は簡略化してある。

Student.java
import java.util.*;

public class Student {
    int no;      //番号
    String name; //氏名
    List<String> kyoukas = new ArrayList<>(); //教科名
    List<String> kamokus = new ArrayList<>(); //科目名
    List<Integer> tanis = new ArrayList<>();  //単位数
    List<Integer> zdns = new ArrayList<>();   //授業日数
    List<String> bikous = new LinkedList<>(); //所見(備考)
    Map<Integer,List<Integer>> tensmap = new HashMap<>(); //成績
    Map<Integer,List<Integer>> kessmap = new HashMap<>(); //欠席

    public Student(int no){
        this(no,"");
    }
    public Student(int no, String name){
        this.no = no;
        this.name = name;
    }
}

HRdata.java

 Studentクラスにデモ用データをセットするためのクラス。9つのファイルから読み込んで、Studentのマップを返す。これもファイルは簡便なテキストファイルにしてある。

HRdata.java
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;

public class HRdata {
    Map<Integer,Student> students = new LinkedHashMap<>();
    int gakki;
    String HR;
    int nendo;
    String kouchou = "小泉 洋二";
    String hrtan = "八木橋 孝";
    public HRdata(int nendo, String HR, int gakki){
        this.gakki = gakki;
        this.HR = HR;
        this.nendo = nendo;
        String pt="data/";
        switch(gakki){
            case 3:
                readSeisekiFile(pt+HR+"5.txt",4);
                readSeisekiFile(pt+HR+"33.txt",3);
                readShukketsuFile(pt+HR+"4-3",4);
                readShukketsuFile(pt+HR+"1-3",3);
            case 2:
                readSeisekiFile(pt+HR+"23.txt",2);
                readShukketsuFile(pt+HR+"8-12",2);
            case 1:
                readSeisekiFile(pt+HR+"13.txt",1);
                readShukketsuFile(pt+HR+"4-7",1);
        }
        readZdnFile(pt+HR+"ZDN");
        String file = pt+String.valueOf(nendo).substring(2,4)+HR;
        String ame  = "tsu"+gakki+".txt";
        readBun(file,ame);
    }
    /**成績ファイル読み込み*/
    private void readSeisekiFile(String fileName, int gakki) {
        try(BufferedReader br = Files.newBufferedReader(Paths.get(fileName))){
            String line;
            line = br.readLine();
            String[] kyoukas = line.split("\t");
            line = br.readLine();
            String[] kamokus = line.split("\t");
            line = br.readLine();
            String[] tanis = line.split("\t");
            while ((line = br.readLine()) != null) {
                //if (line.startsWith("#")) continue;
                String[] tmp = line.split("\t");
                if (3>tmp.length) continue;
                List<Integer> tens = new ArrayList<>();
                for(int i=2;tmp.length>i;i++){
                    if (tmp[i].equals("( )")) tens.add(-1);
                    else if (tmp[i].equals(" ")) tens.add(-3);
                    else tens.add(Integer.parseInt(tmp[i]));
                }
                int no = Integer.parseInt(tmp[0]);
                if(!students.containsKey(no)){
                    students.put(no,new Student(no,tmp[1]));
                }
                Student st = students.get(no);
                st.tensmap.put(gakki,tens);
                if(gakki==1){
                    for(int i=2;kyoukas.length>i;i++){
                        st.kyoukas.add(kyoukas[i]);
                        st.kamokus.add(kamokus[i]);
                        st.tanis.add(Integer.parseInt(tanis[i]));
                    }
                }
            }
        }catch(IOException e){
            System.err.println( e);
        }
    }
    //"17/02/11_11:12:44"
    //"DIM=3 begin with 0 0 1",0,38,6
    //4,9,15,0,6,8
    //2,2,0,0,0,1
    /**出欠ファイル読み込み*/
    private void readShukketsuFile(String fileName, int gakki) {
        try(BufferedReader br = Files.newBufferedReader(Paths.get(fileName))){
            String line;
            line = br.readLine();
            String[] date = line.split(",");
            line = br.readLine();
            String[] dformat = line.split(",");
            line = br.readLine();
            String[] sum = line.split(",");
            int no = 0;
            while ((line = br.readLine()) != null) {
                String[] tmp = line.split(",");
                if (3>tmp.length) continue;
                List<Integer> kess = new ArrayList<>();
                for(int i=0;tmp.length>i;i++){
                    if (tmp[i].equals("")) kess.add(0);
                    else kess.add(Integer.parseInt(tmp[i]));
                }
                no++;
                if(!students.containsKey(no)){
                    students.put(no,new Student(no,""));
                }
                students.get(no).kessmap.put(gakki,kess);
            }
        }catch(IOException e){
            System.err.println( e);
        }
    }
    /**授業日数のファイルを読む*/
    private void readZdnFile(String fileName) {
        try(BufferedReader br = Files.newBufferedReader(Paths.get(fileName))){
            String line = br.readLine();
            String[] tmp = line.split(",");
            List<Integer> zdns = new ArrayList<>();
            for(int i=0;tmp.length>i;i++){
                if (tmp[i].equals("")) zdns.add(0);
                else zdns.add(Integer.parseInt(tmp[i]));
            }
            for (Student st:students.values()) {
                st.zdns.addAll(zdns);
            }
        }catch(IOException e){
            System.err.println( e);
        }
    }
    /** 161A01xxx1.txt から文章データを読む */
    private void readBun( String file, String ame ) {
        for (Student st:students.values()) {
            Path path = Paths.get(file+String.format("%02d",st.no)+ame);
            if (Files.exists(path)){
                try{
                    st.bikous = Files.readAllLines(path, StandardCharsets.UTF_8 );
                }catch (IOException e) {
                    System.err.println(e);
                }
            }
        }
    }

    public static void main(String[] args) {
        int gakki = 3;
        int n = 2;
        HRdata hr = new HRdata(2016,"1J",gakki);
        Student st = hr.students.get(n);
        System.out.println(st.name);
        List tens = st.tensmap.get(gakki);
        List kamokus = st.kamokus;
        System.out.println(kamokus.size());
        for(int i=0;kamokus.size()>i;i++){
            System.out.println(kamokus.get(i)+":"+tens.get(i));
        }
        List kess = st.kessmap.get(gakki);
        for(int i=0;kess.size()>i;i++){
            System.out.println(kess.get(i));
        }
        if(gakki==3){
            tens = st.tensmap.get(gakki+1);
            for(int i=0;kamokus.size()>i;i++){
                System.out.println(kamokus.get(i)+":"+tens.get(i));
            }
            kess = st.kessmap.get(gakki+1);
            for(int i=0;kess.size()>i;i++){
                System.out.println(kess.get(i));
            }
            System.out.println(st.zdns);
        }
    }
}

AdjustString.java

 横位置の均等割付けなどの配置。第4回に掲載したものに、今回禁則処理と姓名の均等割付けを追加した。

AdjustString.java
/** 
指定範囲に文字列を均等割付、右寄せ、左寄せ、センタリングなどの調整をして描画する
入り切らない場合は、入りきる分だけの部分文字列を保持して
均等割付、左寄せなどの指示により部分文字列を描画する
(2017-6-26 version 2.0 
(2019-9-20 version 3.0
@author Adachi
@version 3.0
*/
import java.awt.Graphics2D;
import java.awt.FontMetrics;
import java.awt.image.BufferedImage;

public class AdjustString {
    String zstr, str; //motono string, string
    int   zcpct,cpct; //code point count
    int   bgncpi, nxtbgncpi; //cp index begin, next cp index begin
    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;
    public boolean debug = false; //true;
    /** areawidth に string を入れる時の余り remm を計算 */
    public AdjustString(Graphics2D g2, String string, float areawidth) {
        this(g2,string,areawidth,0);
    }
    /** areawidth に string を入れる時の余り remm を計算。ただし str の bgncpiから */
    public AdjustString(Graphics2D g2, String string, float areawidth, int begincpindex) {
        this.g2 = g2;
        fm = g2.getFontMetrics();
        zstr = (string!=null) ? string : "";  //nullなら""
        wdmm = (areawidth>=0f) ? areawidth : 0f;       //負なら0
        zcpct = zstr.codePointCount(0,zstr.length());  //zstrのcp数
        bgncpi = (begincpindex>=0) ? begincpindex : 0;       //負なら0
        if (zcpct>bgncpi){  //開始位置をindexに換算
            int idxbgn = zstr.offsetByCodePoints(0,bgncpi);
            str = zstr.substring(idxbgn);
        }else{             //開始位置が文字列をはみ出していたら
            str = "";
        }
        remm = wdmm - fm.stringWidth(str)*pt2mm;  //あまりを計算
        if(debug){System.out.println(report());}
        while(0>remm && !str.isEmpty()) {
            int newendidx = str.offsetByCodePoints(str.length(), -1); //一つ前のindexを求める
            str = str.substring(0,newendidx);
            remm = wdmm - fm.stringWidth(str)*pt2mm;
            if(debug){System.out.println(report());}
        }
        cpct = str.codePointCount(0,str.length());
        nxtbgncpi = bgncpi + cpct;
        gapmm = remm/(cpct-1);
    }
    /** 文字列・余り・文字数 をレポートする。(デバッグ用) */
    public String report(){
        cpct = str.codePointCount(0,str.length());
        nxtbgncpi = bgncpi + cpct;
        String rstr = String.format(
            "str=%s /remm=%f /str.length()=%d /cpct=%d /nxtbgncpi=%d",
             str,    remm,    str.length(),    cpct,    nxtbgncpi);
        return rstr;
    }

    /** コンストラクタの直後に呼び出し、
    必要があれば kindic にある文字が次の行頭に来るとき、追い出し処理をする 
    nextjbgn(旧版の変数) -> nxtcpibgn(新版の変数) -> nxtbgncpi */
    public boolean setOidashiIfNeeded(String kindic){
        if (!hasNext()) return false;
        if (5>str.codePointCount(0, str.length())) return false;
        int newidxbgn = zstr.offsetByCodePoints(0,nxtbgncpi);
        String nexttop = zstr.substring(newidxbgn,zstr.offsetByCodePoints(0,nxtbgncpi+1));
        if(debug)System.out.println( 
            "str="+str+ " nxtbgncpi="+nxtbgncpi +" newidxbgn="+newidxbgn);
        if(debug)System.out.println( "nexttop=("+nexttop+")" );
        if ( 0>kindic.indexOf(nexttop) ) return false;
        int newlengthincp = str.codePointCount(0,str.length())-1;
        int newlengthinindex = str.offsetByCodePoints(0, newlengthincp);
        str = str.substring(0, newlengthinindex);
        nxtbgncpi--;
        remm = wdmm - fm.stringWidth(str)*pt2mm;
        gapmm = remm/(newlengthincp-1);
        if(debug)System.out.println( "xstr="+str+ " xnxtbgncpi="+nxtbgncpi );
        return true;
    }

    /** zstr のなかで書ききれなかった残りがあるかを答える */
    public boolean hasNext(){
        return zcpct>nxtbgncpi;
    }
    /** 書ききれなかった残りの文字の開始位置(コードポイントで)を答える */
    public int getNextPt(){
        int retval = -1;
        if (zcpct>nxtbgncpi) retval=nxtbgncpi;
        return retval;
    }
    /** strが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 void drawKintouSx(float hm, float vm) {
        float hpp = 0;
        int strlen = str.length();
        if (cpct>1){
            String[] tmp = str.split(" ");
            int spct = tmp.length-1;
            float gapmmtmp = remm/(cpct-1-spct);
            int i=0;
            int nexti = 0;
            int ct = 0;
            while (strlen>i){ 
                nexti = str.offsetByCodePoints(i,1);
                g2.drawString(str.substring(i,nexti)
                       ,(hm+gapmmtmp*ct)*mm2pt+hpp,vm*mm2pt);
                hpp = fm.stringWidth(str.substring(0,nexti)); //pt when 0?
                if (! str.substring(i,nexti).equals(" ") ) ct++;
                i=nexti;
            }
        }else{
            drawCenter( hm, vm);
        }
    }

    /**マスの中に入れるため、前後に字間の半分のスペースを入れる */
    public void drawInGrid(float hm, float vm) {
        float hpp = 0;
        int strlen = str.length();
        float gapmmtmp = remm/cpct;
        int i=0;
        int nexti = 0;
        int ct = 0;
        while (strlen>i){ 
            nexti = str.offsetByCodePoints(i,1);
            g2.drawString(str.substring(i,nexti),
                    (hm+gapmmtmp*ct+gapmmtmp/2)*mm2pt+hpp,vm*mm2pt);
            hpp = fm.stringWidth(str.substring(0,nexti)); //pt when 0?
            i=nexti;
            ct++;
        }
    }
}

次のページ
データ

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

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

もっと読む

この記事の著者

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

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

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

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

この記事をシェア

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

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング