PHPで異体字を表示する

(1/1)
PHPで異体字を表示する
外字が不要に――ISO/IEC 10646:2017」では、IPA などが進める文字整備基盤事業で、「ISO/IEC 10646:2017」として 6 万字を超える漢字が収録されたことを解説した。その中には、渡邊の「」など、多くの異体字を含んでいる。
そこで今回は、ISO/IEC 10646:2017の異体字を表示する PHP プログラムを作ってみることにする。
(2021 年 1 月 2 日)40 ビット以上の Unicode で文字化けが起きる不具合を修正。

目次

サンプル・プログラムの実行例

PHPで異体字を表示する

サンプル・プログラム

圧縮ファイルの内容
itaiji.phpサンプル・プログラム本体
mji.00601.sqlite3MJ文字情報一覧表(SQLite3データベース)
mji2sql.vbsMJ文字情報一覧表(Excelファイル)からSQLite3データベースを生成するVBScript

準備:MJ文字情報一覧表

IPA のMJ 文字情報取得 APIを利用することで、Unicode(コードポイント)から異体字の MJ 文字図形を求めることができる。
だが、この API は試験的なものであり、2020 年(令和 2 年)8 月、文字情報基盤の成果物が IPA から一般社団法人文字情報技術促進協議会に移管したことから、停止する可能性がある。
そこで今回は、文字情報技術促進協議会に移管された「MJ 文字情報一覧表」を利用することにする。

MJ 文字情報一覧表は、前述の 6 万文字を超える漢字の一覧表である。
Strict Open XML 形式の Excel ファイルとしてダウンロードできるのだが、PhpExcelGoogle Spreadsheet などの既製ライブラリを使ってオープンすることができない。Excel も 2007 ではオープンできず、Microsoft 365 を使ってオープンできることが分かった。
そこで、ダウンロードしたMJ 文字情報一覧表を、Microsoft 365 を搭載した PC 上で VBScript を使って読み込み、SQLite データベースに変換し、PHP から利用できるようにする方針を立てた。

SQLite データベースを生成するため、SQLite ODBC ドライバを用意する。
公式サイトから SQLite ODBC Driver をダウンロードする。64 ビット版でも、32 ビット版の "sqliteodbc.exe" をダウンロードし、インストールするといいようだ。

Microsoft 365SQLite ODBC Driver が用意できたら、次の手順で MJ 文字情報一覧表をデータベースに変換する。
  1. MJ 文字情報一覧表をダウンロードし、解凍する。"mji.00601.xlsx" という約 7.5M バイトのファイルができる。
  2. 同梱の VBScript "mji2sql.vbs" を実行する。MJ 文字情報一覧表ファイルは変数 fnameMJ に、SQLite データベース・ファイルは DBname にフルパスで記述すること。DBname は書き込み権限があるパスを指定する。
  3. しばらくすると、SQLite データベース・ファイルができる。PHP から参照できるフォルダへ移動する。
変換した MJ 文字情報一覧表(SQLite データベース)は、"mji.00601.sqlite3" として同梱しており、これをそのまま利用してもらって構わない。

MJ文字情報一覧表:テーブル定義

mj:MJ文字情報一覧表
No. 名前 内容
1MJcodeテキストMJ文字図形名
2ucsテキスト実装したUCS:プライマリキー
3ivsテキスト実装したMoji_JohoコレクションIVS
4kosekiテキスト戸籍統一文字番号
5jukiテキスト住基ネット統一文字コード
6sesakuテキスト漢字施策:常用漢字、人名漢字
1yomiテキスト読み(参考):音読みは片仮名、訓読みは平仮名。

準備:IPAmj明朝フォント

MJ 文字情報一覧表にある異体字を表示できるのは、IPAmj 明朝フォントである。
文字情報技術促進協議会からダウンロード、解凍すると、"ipamjm.ttf" という約 46M バイトの巨大なフォントファイルが取り出せる。
これを、PHP から参照できるフォルダに配置する。

解説:初期値

0037: //入力文字(デフォルト)
0038: define('DEF_SOUR', '');
0039: 
0040: //MJ文字情報一覧表(SQLiteデータベース)
0041: define('DBFILE', './mji.00601.sqlite3');
0042: 
0043: //検索SQL
0044: define('PRE_SELECT', 'SELECT MJcode, ivs FROM mj WHERE ucs=:ucs;');
0045: 
0046: //PNG画像データURL
0047: define('URL_PNG', 'https://mojikiban.ipa.go.jp/');
0048: 
0049: //IPAmj明朝フォント・ファイルの場所
0050: define('IPAmj', '../../../../common/font/ipamjm.ttf');

冒頭で用意した MJ 文字情報一覧表(SQLite データベース)検索は定数 DBFILE に、IPAmj 明朝フォント・ファイルの場所は定数 IPAmj に記述する。

解説:Unicodeコードポイントを求める

0212: /**
0213:  * Unicodeコードポイントを求める
0214:  * @param string $ch 1文字
0215:  * @return string Unicodeコードポイント
0216: */
0217: function ch2ucp($ch) {
0218:     return sprintf("U+%0X", hexdec(bin2hex(mb_convert_encoding($ch, 'UCS-4', INTERNAL_ENCODING))));
0219: }

1 文字(エンコードは UTF-8 を想定)から Unicode コードポイントを求めるユーザー関数が ch2ucp である。

解説:UnicodeコードポイントからUTF-8文字を求める

0221: /**
0222:  * UnicodeコードポイントからUTF-8文字を求める
0223:  * @param string $ucp Unicodeコードポイント
0224:  * @return string Unicodeコードポイント
0225: */
0226: function ucp2ch($ucp) {
0227:     $ch = '';
0228:     if (preg_match('/U\+([0-9A-F]+)/ui', $ucp$arr) > 0) {
0229:         $bin = hex2bin(str_repeat('0', 8 - strlen($arr[1])) . $arr[1]);
0230:         $ch = mb_convert_encoding($binINTERNAL_ENCODING, 'UCS-4');
0231:     }
0232:     return $ch;
0233: }

ucp2chch2ucp の逆関数で、Unicode コードポイントから UTF-8 文字を求める。

解説:異体字を求める

0235: /**
0236:  * 異体字を求める
0237:  * @param string $ch    1文字
0238:  * @param array  $items 情報を格納する配列
0239:  * @return int 異体字の数/0:指定文字が見つからない/(-1):DBアクセスに失敗
0240: */
0241: function geiItaiji($ch, &$items) {
0242:     $ucp = ch2ucp($ch);
0243: 
0244:     //MJ文字情報一覧表(SQLiteデータベース)検索
0245:     try {
0246:         $pdo = new PDO('sqlite:' . DBFILE);
0247:         $pdo->setAttribute(PDO::ATTR_ERRMODEPDO::ERRMODE_EXCEPTION);
0248:         $stmt = $pdo->prepare(PRE_SELECT);
0249:         $stmt->bindValue(':ucs', $ucpPDO::PARAM_STR);
0250:         $stmt->execute();
0251:         $cnt = 0;
0252:         while ($row = $stmt->fetch()) {
0253:             //画像ファイルのURL
0254:             $items[$cnt]['png'] = URL_PNG . $row['MJcode'] . '.png';
0255:             //HTMLエンティティ
0256:             if (preg_match('/([0-9A-Z]{4,6})\_([0-9A-Z]{5})/ui', $row['ivs'], $arr) > 0) {
0257:                 $items[$cnt]['itaiji1'] = '&#x' . $arr[1] . ';&#x' . $arr[2] . ';';
0258:             } else if (preg_match('/U\+([0-9A-Z]{4,6})/ui', $ucp$arr) > 0) {
0259:                 $items[$cnt]['itaiji1'] = '&#x' . $arr[1] . ';';
0260:             } else {
0261:                 $items[$cnt]['itaiji1'] = '';
0262:             }
0263:             //HTMLエンティティ(エンコード)
0264:             $items[$cnt]['itaiji2'] = preg_replace('/&/ui', '&', $items[$cnt]['itaiji1']);
0265:             $cnt++;
0266:         }
0267:         $pdo = NULL;
0268:     } catch (PDOException $e) {
0269:         $cnt = -1;
0270:     }
0271: 
0272:     return $cnt;
0273: }

ユーザー関数 geiItaiji は、冒頭に用意した MJ 文字情報一覧表(SQLite データベース)を検索し、異体字に関する情報(文字情報技術促進協議会の PNG画像ファイルの URL、異体字の HTML エンティティ)を配列に格納する。

参考サイト

(この項おわり)
header