1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VisualforceページPDFで日本語改行させるときの汎用メソッド

Last updated at Posted at 2024-07-26

PDFで日本語がうまく改行されないやつ

VisualforceページでPDF作るときにどうしてもぶつかるのが、自動改行されない問題。
ある程度文字数で改行しても、等幅フォントではないので英数字で見え方にかなり異なってくる。
今回はその時に利用可能なメソッドの備忘。あくまでも概算なので、もっと厳密にできるとは思うんですが。

サンプルコード

CalcFontWidth.cls
/**
 * テキストエリアのような長文の改行コードをHTML用に変換する。
 * 自動改行されない箇所も指定文字数で改行する
 * @param String 置換前の文字列, Integer 改行したい文字幅(バイト数に係数をかけたものを幅とする:半角 1.25、全角 2.25)
 * @param Integer lbn
 * @return String 置換後の文字列
 */
private String replaceLinebreakCode(String beforeStr, Integer lbn){

    // 置換前文字列チェック
    if( String.isBlank(beforeStr) ){
        return null;
    }
    // 分割文字数チェック
    if( lbn == 0 || lbn == null ){
        return null;
    }
    String afterStr = '';

    // あらかじめ改行されている行を1StringとしてListに格納
    List<String> bfrStrs = new List<String>();
    String linebreak = System.label.LineBreak.replace('-', '');
    beforeStr = beforeStr.replaceAll('\r\n','★改行★').replaceAll('\n','★改行★').replaceAll(linebreak, '★改行★');
    bfrStrs = beforeStr.split('★改行★');

    // 1行ずつループするし、1行が指定文字数に収まるように分割し、返却用文字列を作成する
    for(String str :bfrStrs){
        String line = '';
        Decimal lineBites = 0;
        List<String> splitStr = str.split('');
        // 1文字ずつループする
        for(String oneChar :splitStr){
            // 半角・全角によって前提となる文字幅を調整する
            Decimal fontLength = calcFontWidth(oneChar);

            // 1文字追加した場合に1行に収まるか計算し、収まらなかった場合は次の行に移行する
            if( lineBites + fontLength > lbn){
                afterStr += line;
                afterStr += '★改行★';
                line = oneChar;
                lineBites = fontLength;
            }else{
                line += oneChar;
                lineBites += fontLength;
            }
        }
        afterStr += line;
        afterStr += '★改行★';
    }
    // 一番最後のbrを削除する
    afterStr = afterStr.escapeHtml3().removeEnd('★改行★').replace('★改行★','<br/>');

    return afterStr;
}

/**
 * 全角文字・半角文字(半角の場合はさらに幅分類)別の文字幅を概算する
 * @param String 判定対象文字
 * @return Decimal 文字幅概算
 */
private Decimal calcFontWidth(String oneChar){
    // [Arial Unicode MS]は等幅フォントでないため、著しくサイズが異なる文字のみ別判定する
    String superWideCases = 'mMW';
    String veryWideCases = 'GOQ';
    String wideCases = 'wABCDEFHKNPRSTUVXYZ';
    String narrowCases = 'ftI';
    String veryNarrowCases = 'ijl';

    Decimal fontLength = isHalfChar(oneChar)? 1.25 : 2.25;

    // アルファベットのうち、大文字・太めの小文字・細めの小文字を判定して微調整する
    if( superWideCases.contains(oneChar) ){
        fontLength += 0.9;
    } else if( veryWideCases.contains(oneChar) ){
        fontLength += 0.6;
    } else if( wideCases.contains(oneChar) ){
        fontLength += 0.45;
    } else if( narrowCases.contains(oneChar) ){
        fontLength -= 0.6;
    } else if( veryNarrowCases.contains(oneChar) ){
        fontLength -= 0.75;
    }

    return fontLength;
}

/**
 * 全角文字・半角文字をbyte数にて判断する
 * @param String 判定対象文字
 * @return Boolean 半角文字の場合Trueを返却する
 */
private Boolean isHalfChar(String oneChar){
    Boolean isHalf = false;
    Integer charLength = Blob.valueOf(oneChar).size();
    if( charLength == 1 ){
        isHalf = true;
    }

    return isHalf;
}

使い方

replaceLinebreakCode(String beforeStr, Integer lbn)に改行したい文章と、1行当たりの長さを渡したら、<br/>で改行済みの文字列が返ってきます。
page側では<apex:outputText value="{!userName}" escape="false" />みたいな感じで、escape設定して出力いただくとよいと思います。

備忘

実際に使うときは、Integer lbnで行の長さを調整して、文字幅とかもいい感じに微調整してもらえばいいのかと。
今回はしてないけど、記号が頻出の時は記号幅調整対象にしていけばよろし。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?