縦の均等割付の不具合回避

不具合回避の結果

LandscapeだとFontMetricsが間違った大きさを返すによる不具合の対策です。代替の方法を使用します。

不具合回避 不具合回避後

均等割付に問題 不具合回避前

基本的な考え方

getHeight()がゼロだったら代替にgetFont().getSize()の値を使うというもの。行間分が少ない可能性もあるが、下方向に空白がついている分であることが多いので均等割付では大きな影響はないだろう。上寄せ、下寄せでは文字がくっつく傾向にあるが許容範囲。

プレビューと微妙に異なる可能性があるが、Linuxでなければ出現しないバグなので大きな影響はないだろう。

FontMetrics fm = g.getFontMetrics();
float mojih = fm.getHeight();
float ascent = fm.getAscent();
if (mojih==0 ) {
    mojih = fm.getFont().getSize();
    ascent = mojih * 0.9f;
}

書ける範囲の文字列や余白と同様に、コンストラクタで計算しておいて、メソッドで使います。

対策後のAjustStringT.java

大きな変更ではありません。

AjustStringT.java

package print01;

import java.awt.Graphics2D;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.image.BufferedImage;

public class AjustStringT {
    String kp, nbf;
    int cpct, idxbgn, newcpbgn, nbfcp;
    float fmwm, remm;
    float aidamm = 0f;
    Graphics2D g;
    FontMetrics fm;
    float mm2pt = 72/25.4f;
    float pt2mm = 25.4f/72;
    float dscinpt, ascent, mojih; //dscinpt = fm.getDescent());
    boolean debug = false;
    /** wakuhaba に入る mojiretsu を計算 */
    public AjustStringT(Graphics2D g, String mojiretsu, float wakuhaba) {
        this(g,mojiretsu,wakuhaba,0);
    }
    /** wakuhaba に入る mojiretsu を計算 ただし、kaishiichi 以降の文字列を対象にする */
    public AjustStringT(Graphics2D g, String mojiretsu, float wakuhaba, int kaishiichi) {
        this.g = g;
        kp = (mojiretsu!=null)? mojiretsu:"";  //nullなら""
        fmwm = (wakuhaba>=0)?wakuhaba:0;       //負なら0
        fm = g.getFontMetrics();
        mojih = fm.getHeight();
        ascent = fm.getAscent();
        if (mojih==0 ) {
            mojih = fm.getFont().getSize();
            ascent = mojih * 0.9f;
        }
        cpct = kp.codePointCount(0,kp.length());  //kpのcp数
        if (0>kaishiichi) kaishiichi=0;       //負なら0
        if (cpct>kaishiichi){  //cpをindexに換算
            idxbgn = kp.offsetByCodePoints(0,kaishiichi);
            nbf = kp.substring(idxbgn);
        }else{             //開始位置が文字列をはみ出していたら
            idxbgn = kaishiichi-cpct+kp.length();
            nbf = "";
        }
        if(debug)System.out.println("cpct="+cpct+" /kaishiichi="+kaishiichi+" /idxbgn="+idxbgn+" /nbf="+nbf);//test
        nbfcp = nbf.codePointCount(0, nbf.length());
        remm = fmwm - mojih*pt2mm*nbfcp;  //あまりを計算
        newcpbgn = kaishiichi + nbfcp;  //書いた後の次の文字を計算
        if(debug)System.out.println( "nbf="+nbf+" /remm="+remm +" /nbf.length()="+nbf.length()+" /newcpbgn="+newcpbgn);//test
        while(0>remm && !nbf.isEmpty()) {
            int newendidx = nbf.offsetByCodePoints(nbf.length(), -1); //一つ前のindexを求める
            nbf = nbf.substring(0,newendidx);
            nbfcp = nbf.codePointCount(0, nbf.length());
            remm = fmwm - mojih*pt2mm*nbfcp;
            newcpbgn = kaishiichi + nbfcp;
            if(debug)System.out.println( "nbf="+nbf+" /remm="+remm +" /nbf.length()="+nbf.length()+" /newcpbgn="+newcpbgn);//test
        }
        //ascent = fm.getAscent();
    }
    public static void main( String[] args ) {
        BufferedImage buffimg = new BufferedImage(400,300,BufferedImage.TYPE_INT_RGB);
        Graphics2D g = buffimg.createGraphics();
        g.setFont(new Font(Font.SERIF, Font.PLAIN, 10));
        float wakuhaba = (args.length>0) ? Float.parseFloat(args[0]):10f;
        int kaishiichi = (args.length>1) ? Integer.parseInt(args[1]):0;
        String mojitachi="東a西𠀋南ア北";
        System.out.println("文字列="+mojitachi+" /枠幅(mm)="+wakuhaba+" /開始番号(0-)="+kaishiichi);
        AjustStringT ast = new AjustStringT(g, mojitachi,wakuhaba,kaishiichi);
    }
    public boolean hasNext(){
        return cpct>newcpbgn;
    }
    public int getNextPt(){
        int retval = -1;
        if (cpct>newcpbgn) retval=newcpbgn;
        return retval;
    }
    public float getLastRemm(){
        return remm;
    }
    /**縦書きを左寄せのように上に詰める。hmを中心線にする */
    public void drawTop(float hm, float vm) {
        //float vpp = fm.getHeight(); -> mojih
        int nbflen = nbf.length();
        //int nbfcplen = nbf.codePointCount(0,nbflen);
        int i=0;
        int nexti = 0;
        int cpcti = 0;
        float fontwd;
        while (nbflen>i){
            nexti = nbf.offsetByCodePoints(i,1);
            fontwd = fm.stringWidth(nbf.substring(i,nexti));
            g.drawString(nbf.substring(i,nexti),hm*mm2pt-fontwd/2,vm*mm2pt+mojih*cpcti+ascent);
            i=nexti;
            cpcti++;
        }
    }
    /**縦書きを右寄せのように下に詰める。hmを中心線にする */
    public void drawBottom(float hm, float vm) {
        //float vpp = fm.getHeight();->mojih
        int nbflen = nbf.length();
        //int nbfcplen = nbf.codePointCount(0,nbflen);
        int i=0;
        int nexti = 0;
        int cpcti = 0;
        float fontwd;
        while (nbflen>i){
            nexti = nbf.offsetByCodePoints(i,1);
            fontwd = fm.stringWidth(nbf.substring(i,nexti));
            g.drawString(nbf.substring(i,nexti),hm*mm2pt-fontwd/2,(vm+remm)*mm2pt+mojih*cpcti+ascent);
            i=nexti;
            cpcti++;
        }
    }
    /**縦の均等割付 hmを中心線にする */
    public void drawTtoB(float hm, float vm) {
        //float vpp = fm.getHeight(); -> mojih
        int nbflen = nbf.length();
        int nbfcplen = nbf.codePointCount(0,nbflen);
        if (nbfcplen!=1){
            aidamm = remm/(nbfcplen-1);
            int i=0;
            int nexti = 0;
            int cpcti = 0;
            float fontwd;
            while (nbflen>i){
                nexti = nbf.offsetByCodePoints(i,1);
                fontwd = fm.stringWidth(nbf.substring(i,nexti));
                g.drawString(nbf.substring(i,nexti),hm*mm2pt-fontwd/2,(vm+aidamm*cpcti)*mm2pt+mojih*cpcti+ascent);
                i=nexti;
                cpcti++;
            }
        }else{
            g.drawString(nbf,hm*mm2pt,(vm+remm/2)*mm2pt+ascent);
        }
    }
}

こちらにも対策(TsuchiPrev.java)

部分的に横の均等割付の縦位置を決めるのにgetHeight()を使っていたので同様の対策をします

TsuchiPrev.java の一部

float mojih = g2.getFontMetrics().getHeight();
float ascent = g2.getFontMetrics().getAscent();
if (mojih==0 ) {
    mojih = g2.getFontMetrics().getFont().getSize();
    ascent = mojih * 0.9f;
}

必要な部分は2つでした。各科目の高さ

float padbtm = (vptch-mojih*pt2mm)/2;

最上部の項目の部分の横書き

kp = new AjustString(g2, "科目", hwkm-10f);
kp.drawKintou(hbas+5f,vthtop+(vtdtop-vthtop)/2+mojih*pt2mm/2);

追記:書ける文字数を増やすためにさらに変更

これも大きくはありませんが AjustStringT.java への変更です。fm.getHeight()で得られた文字の高さは行間要素を含むことが多く、大きめになります。縦書きの場合は多少文字がくっついたとしても、詰め込みたいこともあります。

そこで、fm.getFont().getSize() で得られる高さを常用する高さとし、"height"という文字列の引数が与えられ、なおかつfm.getHeight()が0でない時だけ fm.getHeight() の値を使うことにします。通常は"height"以外の文字列を与えてください。

AjustStringT.java の一部(旧)

    public AjustStringT(Graphics2D g, String mojiretsu, float wakuhaba, int kaishiichi) {
        this.g = g;
        kp = (mojiretsu!=null)? mojiretsu:"";  //nullなら""
        fmwm = (wakuhaba>=0)?wakuhaba:0;       //負なら0
        fm = g.getFontMetrics();
        mojih = fm.getHeight();
        ascent = fm.getAscent();
        if (mojih==0 ) {
            mojih = fm.getFont().getSize();
            ascent = mojih * 0.9f;
        }
        cpct = kp.codePointCount(0,kp.length());  //kpのcp数

AjustStringT.java の一部(新)

    public AjustStringB(Graphics2D g, String mojiretsu, float wakuhaba, int kaishiichi, String tatef) {
        this.g = g;
        kp = (mojiretsu!=null)? mojiretsu:"";  //nullなら""
        fmwm = (wakuhaba>=0)?wakuhaba:0;       //負なら0
        fm = g.getFontMetrics();
        mojih = fm.getFont().getSize();
        ascent = mojih * 0.9f;
        if (tatef.equals("height") && fm.getHeight()!=0) {
            mojih = fm.getHeight();
            ascent = fm.getAscent();
        }
        cpct = kp.codePointCount(0,kp.length());  //kpのcp数

TsuchiPrev.java の一部

呼び出す方も引数を合わせる

kp = new AjustString(g2, "科目", hwkm-10f, "any");
kp.drawKintou(hbas+5f,vthtop+(vtdtop-vthtop)/2+mojih*pt2mm/2);