2013-12-17

フォントを事前にレンダリングしてMySQLに登録する方法

Categories: PHP MySQL Webサイト
20131217175626t.jpg

[ PR ]


日本語Webフォントは重い

先日作成した 漢字辞典『さくら』 ですが、使用した書き順フォントが 17MB もあり、Webフォントとしてはほとんど実用的ではありませんでした。

ライセンス的には問題なかったので、いくつか別の方法を検討しました。

フォントの形式を変更 → が、上手く行かず

まず試したのは、フォントが TrueType (.ttf) だったため、まず Web Open Font (.woff) に変更しようとしました。

方法は簡単で、FontForgebrewなどから以下のようにインストールして、フォントを書き出しする際にWeb Open Fontを選択するだけです。

# Macの場合
# Install
brew install fontforge --with-x

# launch
fontforge

しかし、変換時にエラーが起こり、データはできたものの上手く読み込めませんでした。

SVGフォントにしてグリフを分解する → 結局不採用

次に試したのは、SVGフォントに変換することでした。

SVGフォントの利点は、単なるXMLなのでglyphタグを分解して別のフォントにするようなスクリプトを書けば簡単に分解できることです。

やろうと思えば1文字ずつであっても分解できます。

しかし、意外と多い!? Web フォントに対応していない環境 ~2012 年の Web フォント事情 - てっく煮ブログ を読んだところ、SVGフォントは下火だということで不安があり、やめました。

結局事前にレンダリングしてDataURLへ

これまでの方法はどれも納得のいくものではなかったため、事前に画像にレンダリングする方法を考えました。

方法は次の通りです。

準備1: テキスト+フォントを画像化するPHP

まずphpでフォントを画像化します。私の作ったttf_to_png.phpは以下です。

<!-- [ ttf_to_png.php ] -->

<?php
$im = imagecreatetruecolor(300, 300);
$black = imagecolorallocate($im, 0, 0, 0);
$white = imagecolorallocate($im, 255, 255, 255);

imagefilledrectangle($im, 0, 0, 299, 299, $white);
$font = '/path/to/your/font.ttf'; // ここにフォントのパスを指定
$text = $_GET["q"];

$bbox = imagettfbbox(200, 0, $font, $text);
$x = $bbox[0] + (imagesx($im) / 2) - ($bbox[4] / 2) - 30;
$y = $bbox[1] + (imagesy($im) / 2) - ($bbox[5] / 2) - 30;
imagettftext($im, 200, 0, $x, $y, $black, $font, $text);


// Write
Header("Content-type: image/png"); 
imagepng($im);
imagedestroy($im);
?>

ちなみにこのファイルはおそらく http でアクセスしないと動かないので、XAMPPMAMPを使うといいと思います。

このphpを使って、例えば

http://localhost/ttf_to_png.php?q=%e4%be%8b

という風にアクセスすると「例」という漢字がレンダリングされます。

準備2: 画像をBase64に変換するPHP

しかしこのままでは、サーバにアップロードしたときもWebフォントの読み込みの遅さはそのままで、かつ画像のレンダリングという高負荷をかけてしまいます。

そこで次のようにDataUrlに変換します。

<!-- [ ttfpng_to_base64.php ] -->

<?php
function base64_encode_image($filename=string) {
    if ($filename) {
        $imgbinary = file_get_contents($filename);
        return base64_encode($imgbinary);
    }
}

$url = 'http://localhost/lab/ttf_to_png.php?q='.$_GET["q"];
echo base64_encode_image($url);
?>

準備3: Base64生成PHP+漢字データを使ってSQLファイルを生成するスクリプト

これをサーバにアップしても(ry 。ということで次のRubyスクリプトでまずテキストに変換します。

# -----[ gen_png.rb ]--------

require 'uri'
require 'open-uri'

def utf_encode(s)
    s.unpack("U*")[0].to_s(16)
rescue
    nil
end

def utf_decode(s)
    [s.hex].pack("U*")
rescue
    nil
end

table = ARGV[0]
filename = ARGV[1]

open(filename) {|file|
    while line = file.gets
        letter = line.gsub(/\n/,"")
            utf = utf_encode(letter)
            if not utf.nil?
                url = "http://localhost/ttfpng_to_base64.php?q=\#{URI.encode(letter)}"
                data = ""
                open(url) {|f|
                    f.each_line {|line| data += line}
                }

                puts "INSERT INTO #{table} (utf, data) VALUES ('#{utf}', '#{data}');"
            end
    end
}

漢字データについて

ちなみにこのファイルは、テーブル名1行に単漢字を1文字を書いたテキストファイルが必要になりますので、例えば Unihanデータベース などの目的に応じたデータを使い、整形して使います。

ちなみにテキスト整形の方法は企業秘密です(笑)。冗談ですが、長いので省略します。Rubyやsedなどを使えば簡単だと思います。サイズが巨大になるので cat file.txt | head -n 10 などを使うよう気をつけてください。

最後の仕上げ

あとはこのRubyファイルを使って、

ruby gen_png.rb images kanji_data.txt > insert_images.sql

という風にSQLファイルを生成します。

あとは事前にデータベースとテーブルを作っておいて、phpMyAdminなどを使ってこのSQLファイルを挿入します。

このSQLファイルは100MBを超えると思うので、圧縮するか事前にアップロードサイズの上限を増やしておく必要があると思います。

完成したテーブルの使い方

まず次のようなPHPファイルを準備します。

<!-- [ utf_image.php ] -->

<?php
require_once 'conf.php';

$utf = strtolower($_GET['q']);

$db = my_db_connect();

$query = 
    "SELECT utf,data FROM image WHERE utf = '".q($db,$utf)."';";

$result = mysqli_query($db,$query) or die(mysqli_error($db));
$results = array();
while($row = mysqli_fetch_array($result, MYSQL_ASSOC)) {
    $results = $row;
}

mysqli_close($db);

$data = $results[0]["data"];

// echo "<pre>";
// var_dump($results);
// echo "</pre>";

Header("Content-type: image/png"); 
echo base64_decode($data);
?>

あとは上記のPHPファイルを使って、

<!-- [ usage.php ]-->

<?php
    $utf = $_GET["utf"];
?>
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>テスト</title>
</head>
<body>
    <img
        src="<?php baseUrl(); ?>/utf_image.php?q=<?php echo url?>"
        />
</body>
</html>

というようなPHPファイルを設置し、

http://localhost/usage.php?q=4f8b

という漢字でUTFコードを渡してあげれば、画像が生成されます。

おそらく負荷もほとんどなく、レスポンスはほとんど一瞬になると思います。

まとめ

この手法を使える例は限られますが、一文字一文字を別々に使うような場合は使えると思います。

気づけばプロ並みPHP~ショッピングカート作りにチャレンジ!
谷藤賢一
リックテレコム
売り上げランキング: 4,750

コメントはTwitterアカウントにお願いします。

RECENT POSTS


[ PR ]

.