TCPDFで外字をPDFで表示する


PDF出力機能をtcpdfで作成した。ただ、システムの都合上、windowsで作成された外字をPDFに出力させなければならないという大きな課題に直面。調べても、なかなか答えが見えて来なかったので、今回やったことをメモする。

外字とは

そもそも、外字という概念に初めて直面した。外字とは、例で示すとすると、高橋さんの「高」。この「高」にも、色々種類があるのだが、、通称はしご高のように、文字コードでサポートされていないものを指す。
詳しくは、下のリンクをみて下さい。

「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

環境情報

PHP 7.1
tcpdf 6.2

今回は、PHPでの開発だったので、PHPでPDF出力を簡単にできてしまうtcpdfと言うライブラリを使用する。

外字対応前の処理

pdf.php
// PDF出力
$pdf = new \TCPDF();

// フォント追加
$font = new \TCPDF_FONTS();

// 指定したパスのttfファイルをフォントとして使用する
$font = $font->addTTFfont(APPPATH."/font/ipaexg.ttf");

$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);
$pdf->SetMargins(0, 0, 0, true);
$pdf->addPage();

// 出力するフォントをセットする
$pdf->SetFont($font, "", 16);

$name = '髙橋'
$pdf->MultiCell($w, $h, $name, 0, 'L', 0, 0, $x, $y, 0, false, false, false);

// pdfを画面に出力
$pdf->Output("output.pdf", "I");

IPAゴシックを以下サイトから落としてきてあげて、IPAフォントダウンロード
fontディレクトに配置する。これで、指定した位置に、文字を出力することができるのだが・・・
髙が文字化けしてしまう。

問題

どうやら、表示したい文字は、古いシステムからデータをインポートしており、windowsで作られた外字だと判明。独自で用意されているフォントファイルを読み込まないと行けないらしい。

なんだー、それなら、フォントファイルをaddTTFfontで読み込むだけじゃんと、外字フォントをfontディレクトリに配置して同じ処理を走らせてみると。エラーになる。プログラムを追っても、意味のわからないところで落ちているので、どうやら正しくないttfファイルを渡していることになるらしい。(もしくは、引数として、オプションの指定が正しくない?)

色々と方法を探ったのだが、addTTFfontで、ttfファイルを、PHPファイルに変換して、フォントを読み込まなければ、この問題は解決できないようである。

解決策

色々な方法を試していく中で、以下のサイトの方法が役にたった。
TCPDFでTTFフォントを使う為にやった10のこと

  1. githubから、tcpdfを落としてくる
    git clone https://github.com/tecnickcom/tcpdf

  2. toolsディレクトリに移動する
    cd TCPDF-master/tools

  3. 外字が書かれているttfファイルをtoolsフォルダに移動させてくる(ここではEUDC.ttf)
    mv ~/EUDC.ttf .

  4. addTTFfontコマンドを使って、tcpdfのフォントファイルを作成する
    php ./tcpdf_addfont.php -b -t eudc -f 32 -i eudc.ttf

これによって、TCPDF-master/fontsの下に、eudc.php、eudc.ctg.z、eudc.zファイルが生成される。

実際にプログラムの中に組み込む

あとは、このフォントをプログラム内に組み込むだけ。PDF出力をしているapp配下のtcpdf/fonts内に、先ほど、生成した3つのファイルを移動させて、セットする。

$pdf->SetFont("EUDC", "", 16);

これで、「髙」が文字化けされずに表示された。
ただ、このフォントをセットするだけだと、通常の文字(外字でないもの)が今度は文字化けして表示される。これは、外字フォントには、外字用の文字コードしか入っていないため。

そのため、外字を出力する時は、外字フォント、それ以外は、IPAフォントを使うように処理をする。

外字の文字コードの範囲は、すでに決まっているので、それを使って判別。

外字判定
public fuction _switch_font()
{
   $use_font = 'ipaexg';

   // 正規表現で、外字判定
   if (preg_match('/^(\xEE[\x80-\xBF])|(\xEF[\x80-\xA3])|(\xF3[\xB0-\xBF])|(\xF4[\x80-\x8F])/', $string))
   {
     $use_font = 'EUDC';
   }

     return $use_font;
   }
}

この外字判定処理を使って、表示させたい名前を1文字単位で、外字かチェックし、
フォントを使い分ける。

pdf.php

$name = '髙橋'

// 文字数(漢字数)を取得
$textLength = mb_strlen($name, 'UTF-8');

// 1文字ずつ外字チェックし、出力
for($i = 0; $i < $textLength; $i++)
{
    // 名前から一文字を取得
    $string = mb_substr($name, $i, 1, 'UTF-8');

    // 外字か判定し、出力するフォントを切り替え
    $use_font = $this->_switch_font($string);

    $pdf->SetFont($use_font, "", 16);
    $pdf->MultiCell($w, $h, $string, 0, 'L', 0, 0, $x, $y, 0, false, false, false);

    // 出力位置をずらす
    $x += 7;
}

// pdfを画面に出力
$pdf->Output("output.pdf", "I");

これで、髙橋が文字化けせずにPDF出力できた!

終わりに

今回は、力技で外字をPDF出力させたけど、もっとスマートなやり方がきっとあるはず。
ttfファイルを外字フォント、IPAフォントをマージさせた形式で作成し、それを1回で読み込んだりもできるのではないか。もしくは、tcpdfのメソッドとして、今回の処理をさらに簡単に実装できるのではないか。そんな風にも思うので、なんかモヤっとします。さらにいい方法があれば、教えていただけるとありがたいです。