PHPで異体字を表示する

(1/1)
PHPで異体字を表示する
外字が不要に――ISO/IEC 10646:2017」では、IPA などが進める文字整備基盤事業で、「ISO/IEC 10646:2017」として 6 万字を超える漢字が収録されたことを解説した。その中には、渡邊の「」など、多くの異体字を含んでいる。
そこで今回は、ISO/IEC 10646:2017の異体字を表示する PHP プログラムを作ってみることにする。
(2021 年 5 月 9 日)画像表示を文字情報技術促進協議会サイトへ更新。
(2021 年 2 月 6 日)-g オプションを追加。
(2021 年 1 月 2 日)40 ビット以上の Unicode で文字化けが起きる不具合を修正。

目次

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

PHPで異体字を表示する
ホームページ上で異体字を画像として表示したいときなど、URL パラメータで -g オプション を使うことで実現できる。
たとえば

<img src="itaiji.php=?g=908A_E0109" /> は 邊 の異体字です。


は 邊 の異体字です。
のようにして使うことができる。-g オプション のあとに、IVS(後述)を指定する。

サンプル・プログラム

圧縮ファイルの内容
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: define('PRE_SELECT_IVS', 'SELECT MJcode, ivs FROM mj WHERE ivs=:ivs;');
0046: 
0047: //PNG画像データURL
0048: //define('URL_PNG', 'https://mojikiban.ipa.go.jp/');
0049: define('URL_PNG', 'https://moji.or.jp/mojikibansearch/img/MJ/');
0050: 
0051: //IPAmj明朝フォント・ファイルの場所
0052: define('IPAmj', '../../../../common/font/ipamjm.ttf');

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

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

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

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

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

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

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

解説:コードポイントから異体字を求める

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

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

解説:IVSから異体字を求める

0277: /**
0278:  * 異体字を求める:IVSから
0279:  * @param string $ivs   IVS
0280:  * @param array  $items 情報を格納する配列
0281:  * @return int 異体字の数/0:指定文字が見つからない/(-1):DBアクセスに失敗
0282: */
0283: function geiItaijiIVS($ivs, &$items) {
0284:     //MJ文字情報一覧表(SQLiteデータベース)検索
0285:     try {
0286:         $pdo = new PDO('sqlite:' . DBFILE);
0287:         $pdo->setAttribute(PDO::ATTR_ERRMODEPDO::ERRMODE_EXCEPTION);
0288:         $stmt = $pdo->prepare(PRE_SELECT_IVS);
0289:         $stmt->bindValue(':ivs', $ivsPDO::PARAM_STR);
0290:         $stmt->execute();
0291:         $cnt = 0;
0292:         while ($row = $stmt->fetch()) {
0293:             //画像ファイルのURL
0294:             $items[$cnt]['png'] = URL_PNG . $row['MJcode'] . '.png';
0295:             //HTMLエンティティ
0296:             if (preg_match('/([0-9A-Z]{4,6})\_([0-9A-Z]{5})/ui', $row['ivs'], $arr) > 0) {
0297:                 $items[$cnt]['itaiji1'] = '&#x' . $arr[1] . ';&#x' . $arr[2] . ';';
0298:             } else if (preg_match('/U\+([0-9A-Z]{4,6})/ui', $ucp$arr) > 0) {
0299:                 $items[$cnt]['itaiji1'] = '&#x' . $arr[1] . ';';
0300:             } else {
0301:                 $items[$cnt]['itaiji1'] = '';
0302:             }
0303:             //HTMLエンティティ(エンコード)
0304:             $items[$cnt]['itaiji2'] = preg_replace('/&/ui', '&amp;', $items[$cnt]['itaiji1']);
0305:             $cnt++;
0306:         }
0307:         $pdo = NULL;
0308:     } catch (PDOException $e) {
0309:         $cnt = -1;
0310:     }
0311: 
0312:     return $cnt;
0313: }

ユーザー関数 geiItaijiIVS は、-g オプション のために、IVS をキーにして MJ 文字情報一覧表(SQLite データベース)を検索し、異体字に関する情報(文字情報技術促進協議会の PNG画像ファイルの URL、異体字の HTML エンティティ)を配列に格納する。

参考サイト

(この項おわり)
header