[BlueLeaf1336]> PROBLEMS> FontStudy>

フォント勉強会その1

historyTOP

2009/11/11:作成
2009/11/18: GetTextMetrics をテスト
2009/11/19: GetGlyphIndices
2009/12/01: OrdEx, ChrEx, OrdW

はじめにTOP

すごい久しぶりに、Delph Win32 Graphics API リファレンス(以降、テキスト)を眺めていて、TTextMetric という構造体を見つけました。フォントに関する情報が格納される構造体だけど、そのメンバーに tmFirstChartmLastChar というのがあって、説明によるとフォントの中で定義されている最初の文字と最後の文字のことらしい。

で、この構造体にフォントの情報を取得するには、GetTextMetrics というAPIを使うこともわかりました。

ところで、イラスト系のフォント(Dingbat?)をインストールしておもしろそうなイラストを文字をでたらめに打って探しているとよくあるのが、□に襲われ続けるというアレです。特に、前に見たことのあるアレってどの文字やったっけ? っていうときにすごく腹がたってきます。

まあ、IMEパッドで文字の一覧から探せばすむわけだけれど、そこはそれ、いつものようになにかしてみようじゃないかと。結構長い間、作って見たいプログラムがなかったけれども、久しぶりにキタ気がします。

とりあえずの目標は、「指定したフォントで定義されている文字だけ出力してみる」です。フォントファイルが莫大な日本語フォントで試すとえらいことになりそうですが、個人レベルで作成されている量なら列挙してもプログラムが沈黙してしまうことはないんじゃないかと。

で、妄想レベルの手順は

少し試してみました。予想通り問題が出ました。それは、ある文字と tmDefaultChar が同じかどうかを調べる方法です。文字コードは当然違います(でないと、違うフォントに切り替えたときにちゃんと出なくなってしまうので)。

じゃあ、どうやって同じかどうか調べるん? てなわけです。パッと思いついたのは、2つの文字を書いてみて、画像的に同じかどうかを調べるというものですが、やりたくないです。邪魔くさそうで遅そう。

同じ理由で、GetPath を使っての比較も嫌。最終的な比較は、フォントのアウトライン化 で取得できることを確認した TPointとByteの配列で比べられそうですが、やっぱり1回 Canvas に書き出す必要があります。

再び、テキストをめくってみました。

GetGlyphOutline というAPIが使えそうです。TGlyphMetrics という構造体と、フォントが設計されたときに作られた直線と曲線の集合(配列と思われ)を、文字を出力せずに取得できるようです。

GetTextMetricsTOP

まずは、GetTextMetrics 実際には、GetTextMetricsW ですが、このAPIを実行して選択したフォントで定義されている最初の文字から最後の文字までをテキストファイルに書き出してみました。

結構勉強になるので、構造体のメンバーにどんな値が入っているかとかを見ておくことにしたのですが、画面構成などテキストをまんまパクってます。ただ、それだと面白くないので、文字の書き出しもやってみたのですが、これがどうもよくわからない。

20091118_FontStudy_src.zip(194,360bytes)

var
    i: WideChar;
    ………
begin
    ………
    //  ファイルストリーム作成
    LStream := TFileStream.Create(LblPath.Caption, fmCreate or fmShareDenyWrite);
    try
        //  最初の文字から最後の文字まで
        for i := LTextMetrics.tmFirstChar to LTextMetrics.tmLastChar do
        begin
            //  制御文字はスキップ
            if (IsCtrlChar(i)) then Continue;
            //  ストリームに書き出し(これで本当にいいのかどうか)
            LStream.Write(i, SizeOf(i));
        end;
    finally
        LStream.Free();
    end;
    ………
end

結局のところ何が出力されてるんだろう。それ以前に、この書き方でちゃんと出力されるんだろうか。出力ファイルを PeggyPad で開けると、文字コードが Unicode LE (って何?) と判定されている。また、65000文字を超える分は切り落とされるわけだけれども、すごい量の「?」(クエスチョンマーク)が出てくる。Peggy が表示していないだけなのか、根本的に変なのか。

ま、悩んだところでアレなんで、次にいきます。

GetGlyphIndicesTOP

今日の昼休み、会社で GetGlyphOutline をぐぐってると、このページの最初に書いたことをまるままやってる人 - フォントで定義されている文字を判定するために、ある文字と未定義文字のための tmDefaultChar の描画情報を比べて一致しているかどうかを調べる - がいました。しかも尺度違いかなにかで無理ということまで読んでしまいました。

今、オフラインなので、URL はわからないのですが、質問板だったので回答がついてました。GetGlyphIndices を使えばわかると。

やってみたんですが、GetGlyphIndices の使い方がわからん。残念。

OrdEx, ChrEx, OrdWTOP

前回の GetGlyphIndices でつまづいてから、全く進んでません。毎日なにかやろうとしてすぐやめてます。

とにかくDelphiを忘れてしまってます。もともとそんなにはできませんが、いよいよ、という感じです。

このページの最初で、GetGlyphOutline を試しましたが、そのときにGetGlyphOutlineWを使いました。人生初の UNICODE (なんだと思うんですけどそれも自信なし) で全編攻めようと思ったからですが、文字コードって本当にわからない。これまでに使おうと思ったことがないのも原因だし、理解するのがしんどくなってる。そんなこんなで、追い追い行くことにします。で、とりあえず普通の GetGlyphOutline から。

で、その引数に文字を渡すんですけど、文字で渡さずに Cardinal で渡すことになってます。これができない... たとえば、"あ" はナンボやねん。Ord できひんし。というわけで、Delphi ML の過去ログめくりまくって関数にしました。

//-----------------------------------------------------------------------------
//  2バイト対応のOrd関数(2文字目以降は無視)
//  Subject: [Delphi:53852] RE:  文字コードの取得方法
function OrdEx(const Char: String): Word;
var
    i: Integer;
begin
    Result := 0;
    if (Char <> '') then
    begin
        for i := 1 to Length(Char) do
        begin
            case ByteType(Char, i) of
            mbSingleByte, mbTrailByte:
            begin
                Result := (Result shl 8) + Ord(Char[i]);
                Break;
            end;
            mbLeadByte:
                Result := Ord(Char[i]);
            end;
        end;
    end;
end;

//-----------------------------------------------------------------------------
//  2バイト対応のChr関数
function ChrEx(const Char: Word): String;
begin
    if (Char and $ff00 > 0) then
        Result := Chr(Hi(Char)) + Chr(Lo(Char))
    else
        Result := Chr(Char);
end;

ちなみに、GetGlyphOutlineW に渡す場合は、以下でいけました。

//-----------------------------------------------------------------------------
//  W 用のOrd関数(2文字目以降は無視)
function OrdW(const Char: String): Word;
begin
    Result := 0;
    if (Char <> '') then Result := Ord(WideString(Char)[1]);
end;

以下が、それぞれの関数の戻り値ですが、IMEパッドのヒントに表示される文字コードと一致してます。かなりの確率で、いまさらっポイですけど、うれしいです。ただ、ChrW (OrdW の逆) はかけませんでした。PlatformSDK をめくると、VB の関数にあるみたいですけど...

 OrdEx("あ")OrdW("あ")
戻り値$82A0$3042

気になるのは、Word で処理していることですけど、IMEパッドの一番後ろにある漢字が、$fc4b なので OK としときます。それより他に考えることいっぱいあるだろうし。

何にもしてないのに、それなりの行数になってきたので、このページはこの辺で。

EOFTOP