この記事では英数字と日本語が混在しそれぞれ異なるフォントを用いた場合の文字列の見た目の幅をPHPで測る方法を紹介します。
文字列の見た目の幅をPHPで測る時、私的に信用できる方法の一つは imagettfbbox 関数を用いる方法です。
imagettfbbox を使うことによって、あるフォントであるサイズで文字列を作った場合にどの様な箱に収まる大きさになるかを知ることができます。これを使うと次の様になります。
<?php /** * 0 左下角の X 座標 * 1 左下角の Y 座標 * 2 右下角の X 座標 * 3 右下角の Y 座標 * 4 右上角の X 座標 * 5 右上角の Y 座標 * 6 左上角の X 座標 * 7 左上角の Y 座標 * @var array $textBox ピクセル単位の座標 */ $textBox = imagettfbbox( 11,// フォントサイズ(pt) 0, // 回転角度(度単位)。 0 が水平 "C:\\Windows\\Fonts\\Calibri.ttf",// 測る文字列に使うフォントのファイルパス 'ほげほげ'// 対象の文字列 ); $widthPx = $textBox[2] - $textBox[0];// 右下X座標 - 左下X座標 echo $widthPx; // 32
この方法は便利なのですが、一つの文字列に対して一つのフォントしか指定できないという制約があります。これが問題となるのは例えば日本語はフォントA、英語はフォントBを使いたいといった場合です。そのようなシチュエーションに対応するためには、文字列を日本語部分と英語部分に分ける必要があります。これは次でできます。
$text = 'ほげabcほdefげ'; // マルチバイトを考慮した一文字ずつの配列に分解 $charList = mb_str_split($text); // シングルバイト文字とマルチバイト文字それぞれを入れる配列を用意 $singleByteCharList = $multiByteCharList = []; foreach($charList as $char){ // strlen はバイト単位で文字の長さを見るため、シングルバイト文字なら1、マルチバイト文字なら2以上が返ってくる if(strlen($char) === 1) { // ↑の if でシングルバイト文字とマルチバイト文字を振り分け $singleByteCharList[] = $char; }else{ $multiByteCharList[] = $char; } } // 文字の配列を結合して文字列化 $singleByteString = implode('', $singleByteCharList); $multiByteString = implode('', $multiByteCharList); // echo 結果の様にいい感じに分離します echo $singleByteString . "\n"; // abcdef echo $multiByteString . "\n"; // ほげほげ
このように文字列をシングルバイト文字とマルチバイト文字に分離することで、日本語と英語をそれぞれ異なるフォントについて文字列の見た目の幅を測る準備ができます。
先述した文字列の見た目の幅を測る方法と上記のマルチバイト文字とシングルバイト文字を分離する方法を組み合わせることによって、次の様に混在するフォントを使用した文字列の見た目の幅を計算することができます。
<?php function getStringWidth(string $text, int $size, string $fontPath): float { /** * 0 左下角の X 座標 * 1 左下角の Y 座標 * 2 右下角の X 座標 * 3 右下角の Y 座標 * 4 右上角の X 座標 * 5 右上角の Y 座標 * 6 左上角の X 座標 * 7 左上角の Y 座標 * @var array $textBox ピクセル単位の座標 */ $textBox = imagettfbbox( $size,// フォントサイズ(pt) 0, // 回転角度(度単位)。 0 が水平 $fontPath, $text ); return $textBox[2] - $textBox[0]; } function stringSeparateByByte(string $text): array { // マルチバイトを考慮した一文字ずつの配列に分解 $charList = mb_str_split($text); // シングルバイト文字とマルチバイト文字それぞれを入れる配列を用意 $singleByteCharList = $multiByteCharList = []; foreach($charList as $char) { // strlen はバイト単位で文字の長さを見るため、シングルバイト文字なら1、マルチバイト文字なら2以上が返ってくる if(strlen($char) === 1) { // ↑の if でシングルバイト文字とマルチバイト文字を振り分け $singleByteCharList[] = $char; } else { $multiByteCharList[] = $char; } } // 文字の配列を結合して文字列化 $singleByteString = implode('', $singleByteCharList); $multiByteString = implode('', $multiByteCharList); return [$singleByteString, $multiByteString]; } function main(){ $text = 'ほげabcほdefげ'; // 単独フォントで実行 $sw = getStringWidth($text, 11, "C:\\Windows\\Fonts\\Calibri.ttf"); echo $sw . "\n"; // 74 $mw = getStringWidth($text, 11, "C:\\Windows\\Fonts\\msgothic.ttc"); echo $mw . "\n";// 148 // 文字列を分離してそれぞれの幅を合成 [$s, $m] = stringSeparateByByte($text); $sw = getStringWidth($s, 11, "C:\\Windows\\Fonts\\Calibri.ttf"); $mw = getStringWidth($m, 11, "C:\\Windows\\Fonts\\msgothic.ttc"); echo $sw + $mw;// 98 } main();
main()関数内で文字列の分離とそれぞれの文字列の幅を計算する処理を行っています。stringSeparateByByte()関数でシングルバイト文字列とマルチバイト文字列に分離し、それぞれの文字列の幅をgetStringWidth()関数で計算しています。その後、シングルバイト文字列とマルチバイト文字列の幅を合計することで、最終的な文字列の幅を取得しています。