【PHP】文字列の幅の測り方とASCII文字と日本語の振り分け方

  • 2022年2月23日
  • 2022年2月24日
  • PHP

 日本語英語混じりでフォントも混じった文字列の幅の測り方を紹介します。

 文字の幅を PHP で測る時、私的に信用できる方法の一つは imagettfbbox 関数を用いてあるフォントであるサイズで文字列を作った場合、どの様な箱に収まる大きさになるかを知る方法です。
PHP: imagettfbbox – Manual
 これを使うと次の様に文字列の幅を得られます。

<?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

 Excel や PDF 等のブラウザが使えない状況では稀にこれに頼ることがあります(大体ライブラリがこれを使用目的に応じていい感じにラッピングしてくれているので、そのラッパーメソッドを使うことの方が多いです)。

 これは便利なのですが一つのフォントしか使えないという点で少々不便です。というのも日本語はフォント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げ';

    // 単独フォントで実行
    $singleByteWidth = getStringWidth($text, 11, "C:\\Windows\\Fonts\\Calibri.ttf");
    echo $singleByteWidth . "\n"; // 74
    $multiByteWidth= getStringWidth($text, 11, "C:\\Windows\\Fonts\\msgothic.ttc");
    echo $multiByteWidth. "\n";// 148

    // 文字列を分離してそれぞれの幅を合成
    [$singleByteStr, $multiByteStr] = stringSeparateByByte($text);
    $singleByteWidth = getStringWidth($singleByteStr, 11, "C:\\Windows\\Fonts\\Calibri.ttf");
    $multiByteWidth= getStringWidth($multiByteStr, 11, "C:\\Windows\\Fonts\\msgothic.ttc");
    echo $singleByteWidth + $multiByteWidth;// 98
}

main();
>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG