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
で行の長さを調整して、文字幅とかもいい感じに微調整してもらえばいいのかと。
今回はしてないけど、記号が頻出の時は記号幅調整対象にしていけばよろし。