[要望] DirectWriteが有効な状態でのカスタムフォントの利用

  1. 初めまして。いつもMeryを便利に使わせて貰っています。

    私は、とある大学の講義でHTML/CSS/JavaScriptを教えており、学生が持参するUSBメモリにMeryを
    インストールさせて、「自宅のPCでも大学のPCでも同じように作業できる」環境を作らせています。
    この大学のコンピューター教室のPCは、利用者がソフトをインストールできないことはもちろん、
    起動時にレジストリやユーザーフォルダーの内容がまっさらに初期化されてしまうため、ポータブルで
    運用できるMeryの存在はとても助かります。

    さて、こうした利用者がWindowsの管理者権限を持たない状況で、
    システムにインストールされていないフォント(以下カスタムフォントを呼びます)を、
    「DirectWriteを有効にした状態で」利用する方法はありませんか。

    以前、カスタムフォントをMeryで利用する方法については、
    >【要望】フォントファイル(ttf)の直接指定
    https://www.haijin-boys.com/discussions/2550
    でディスカッションがあり、別のツール(NexusFontなど)を利用する方法が提示されていました。

    これらのツールは、WinAPIのAddFontResource()などを呼べば、GDIのシステムフォントコレクションに
    (PCを再起動するか、RemoveFontResouce()などで取り除くまで)、指定したフォントが追加された
    状態になることを利用しています。
    私の場合は、AddFontResource()/RemoceFontResouce()を呼ぶだけのコマンドラインツールを自作して、
    Meryの起動用バッチファイルに組み込んで使っています。

    しかし、この方法で追加したカスタムフォントは、DirectWriteのシステムフォントコレクションには
    追加されないので、MeryでDirectWriteを有効にした状態では利用できません。

    実際に、Meryのオプション設定でカスタムフォントを指定すると、
    ・[詳細]で「DirectWriteを有効にする」をチェックしない場合...カスタムフォントが正常に使われる
    ・[詳細]で「DirectWriteを有効にする」をチェックした場合...別のフォント(メイリオか?)が使われる
    という動作になります。

    おそらく、DirectWriteのGetSystemFontCollection()で取得するDirectWriteのシステムフォント
    コレクションに、ユーザーが指定したフォントが含まれないため、Meryが代替フォントを使っている
    のでしょう。

    結果として、
    ・USBメモリなどで持ち込んだカスタムフォントをGDIによる描画で使う
    ・DirectWriteを有効にした状態で、システムにインストール済みのフォントを使う
    の二択になってしまい、DirectWriteによる美しい表示で、コーディングに適したフォントを使うことが
    できません。

     |  Daregada  |  返信
  2. はじめまして。Mery をご愛用くださりありがとうございます。

    ご指摘の通り AddFontResource で追加したフォントは DirectWrite からは使用できないようです。Microsoft のドキュメントによると DirectWrite では自動的にコレクションの更新が監視されるとの記載がありますが、Windows の不具合か仕様かわかりませんが機能していないような感じです。

    DirectWrite 関連の処理において目安にしているのは Chrome や FireFox の挙動ですが、これらのアプリケーションでも AddFontResource で追加したフォントは描画できないようですね。

    ただ、秀○エディタでは AddFontResource で追加したフォントも DirectWrite で描画できているように見えますので、何か裏技があるのかもしれません。

    調査してみましたが残念ながら現状、対策は見つかっておりません。

     |  Kuro  |  返信
  3. お返事ありがとうございます。

    >Microsoft のドキュメントによると DirectWrite では自動的にコレクションの更新が監視されるとの記載がありますが、Windows の不具合か仕様かわかりませんが機能していないような感じです。

    私もそのドキュメント(「Win32 テキスト API の比較」かな?)を読みましたが、コレクションを監視して「何がどうなる」のか書いてないので意味不明ですよね。

    おそらく秀丸は、GDIのシステムフォントコレクションにあって、DirectWriteのシステムフォントコレクションにないフォントに対して、そのドキュメントのひとつ上の欄に書いてある「CreateCustomFontFileReference + CreateCustomFontCollection」を使って、自力で(秀丸が利用する)フォントコレクションに追加しているのではないかと想像しますが、想像するだけでも面倒でやりたくない部類の処理ですね(秀丸のソースも見れないし)。DirectWriteは、使う分には見た目が綺麗でいいのですが、プログラムを組む方には面倒なことが多過ぎです。

    なんとかInconsolata系のフォントを使いたかったのですが、今回は「MS ゴシックのポイント大きめ(16.5ptとか)+Natural Symmetric」で間に合わせます。ありがとうございました。

     |  Daregada  |  返信
  4. ご連絡ありがとうございます。
    DirectWrite にお詳しいのですね、助かります^^

    > 私もそのドキュメント(「Win32 テキスト API の比較」かな?)を読みましたが、コレクションを監視して「何がどうなる」のか書いてないので意味不明ですよね。

    はい、「Win32 テキスト API の比較」です。
    文面のまま受け取ると AddFontResource で追加されたフォントはシステムフォントに追加されるので自動で DirectWrite 側にも反映されるように思えるのですけどね…。実際に GetSystemFontCollection (checkForUpdates = true) で一覧を出力してみたりしましたが全然反映されてませんでした。

    > 「CreateCustomFontFileReference + CreateCustomFontCollection」を使って、自力で(秀丸が利用する)フォントコレクションに追加しているのではないかと想像します

    「CreateCustomFontFileReference + CreateCustomFontCollection」の方式はフォントファイル (*.ttf など) を直接指定して独自のフォントコレクションを作成するのだと思いますが、秀○さんの動作を観察すると、フォントファイルの実体をリネームして秀○さんを再起動したりしても引き続き DirectWrite で使用できているので、やはり AddFontResource で追加したフォントを DirectWrite と関連付けている仕組みが OS 的に存在しているのではないかと思っちゃったりするのですが、謎ですね。

    > なんとかInconsolata系のフォントを使いたかったのですが、今回は「MS ゴシックのポイント大きめ(16.5ptとか)+Natural Symmetric」で間に合わせます。ありがとうございました。

    そうですか。
    ただ、この案件は技術的にも興味深いものですので引き続き調査はしていこうと思いますので、お気づきの点などございましたらご協力いただけると幸いです。

     |  Kuro  |  返信
  5. > 秀○さんの動作を観察すると、フォントファイルの実体をリネームして秀○さんを再起動したりしても引き続き DirectWrite で使用できているので、

    あー、本当だ。適当なこと言ってすいません。

    > やはり AddFontResource で追加したフォントを DirectWrite と関連付けている仕組みが OS 的に存在しているのではないかと思っちゃったりするのですが、謎ですね。

    明日から講義なのでアレなんですが、悔しいのでちょっと調べてみました。AddFontResource() で追加したフォントのLOGFONTを作ってから、IDWriteGdiInterop::CreateFontFromLOGFONT() でDirectWrite用のフォント情報に変換すればいけるかな?

    MSDN(日本語) CreateFontFromLOGFONTの解説
    https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd371187%28v=vs.85%29.aspx

    実際のコーディングに使われている例(Qtのフォントマネージャー?)
    http://code.metager.de/source/xref/lib/qt/src/gui/text/qfontdatabase_win.cpp

     |  Daregada  |  返信
  6. 情報ありがとうございます。
    ほんと、この謎が解けないのは悔しいです…

    実は Mery の内部では CreateFontFromLOGFONT で DirectWrite 用のフォントを作っています。
    しかしながら AddFontResource で登録したフォントの LOGFONT を渡してやっても CreateFontFromLOGFONT はエラーを返してくるので、代替フォントとして「メイリオ」を使っています。

    有名どころのテキストエディタ「Vim」 関連の情報で、似たような内容のものが見つかりました。
    https://github.com/dcpurton/regfont/issues/1

    結局解決してなさそうですが…^^;

    私もあれから気になっていろいろ考えてみたのですが、GDI のフォントからフォントファイル (*.ttf とか) へのパスを引っ張ってきて DirectWrite で CreateFontFileReference してやればいいかも、と思いましたが GDI フォントからファイルの実体へのパスを取得する方法が、レジストリを見るという方法しか見つからず、しかも AddFontResource で追加したフォントはレジストリにすら入っていないという。

    システムフォントテーブル (AddFontResource で追加された情報) というものの実体ってどこにあるのでしょうね…
    ちょっと手詰まりの予感です。

    うーむ、気になります。

     |  Kuro  |  返信
  7. > 実は Mery の内部では CreateFontFromLOGFONT で DirectWrite 用のフォントを作っています。
    > しかしながら AddFontResource で登録したフォントの LOGFONT を渡してやっても CreateFontFromLOGFONT はエラーを返してくるので、代替フォントとして「メイリオ」を使っています。

    > 有名どころのテキストエディタ「Vim」 関連の情報で、似たような内容のものが見つかりました。
    https://github.com/dcpurton/regfont/issues/1

    はい。私もこれを後で見付けました。
    自分で作らなくてもあったやん > コマンドラインでフォントを追加するツール

    > 私もあれから気になっていろいろ考えてみたのですが、GDI のフォントからフォントファイル (*.ttf とか) へのパスを引っ張ってきて DirectWrite で CreateFontFileReference してやればいいかも、と思いましたが GDI フォントからファイルの実体へのパスを取得する方法が、レジストリを見るという方法しか見つからず、しかも AddFontResource で追加したフォントはレジストリにすら入っていないという。

    「GDI フォントからファイルの実体へのフルパスを取得する方法」、ついに見付けましたよ。もちろん、AddFontResource() で追加したフォントも、RemoveFontResource() するまでの間はフルパスが取得できています。

    Windows SDK 7.1 に付属の DirectWrite のサンプル「GdiInterop.cpp」を改造して試行錯誤していたのですが、CreateFontFromLOGFONT() が失敗する状況でもフルパスを取得できるので、,おそらくそれを CreateFontFileRefference() してやれば、AddFontResource() で追加したフォントを DirectWrite で描画できるかも(描画まではまだやっていません)。

    ソースは、まだリソースの開放もろくにしていないような「みせられないよ」状態なので、とりあえずフルパス取得までのフローを示します。

    (0) 前提条件
    ・対象フォントの論理フォント(LOGFONT)かフォントハンドル(hFont)を取得済み
    ・描画先のデバイスコンテキストのハンドル(hDC)を取得済み
    ・DirectWrite のファクトリオフジェクトで IDWriteGdiInterop オブジェクトへのポインタを取得済み
    の3つ。すでに CreateFontFromLOGFONT() を使っているなら大丈夫ですよね。

    (1) 対象フォントのフォントハンドルをデバイスコンテキストで選択する
    // hFont = CreateFontIndirect(&lf);  // LOGFONT情報しかない場合
    SelectObject(hDC, hFont);

    (2) 対象フォントの IDWriteFontFace 情報へのポインタを CreateFontFaceFromHdc() で取得する
    IDWriteFontFace* pFontFace;
    g_pGdiInterop->CreateFontFaceFromHdc(hDC, &pFontFace);  // pFontFace に IDWriteFOntFace 情報へのポインタが設定される

    (3) 対象フォントの IDWriteFontFile 情報へのポインタを GetFiles() で取得する
    UINT32 numberOfFiles;  // 受け取るIDWriteFontFile情報の個数(実際には1しか来ないのですが)
    IDWriteFontFile *pFontFiles[1];  // 複数受け取れるように配列を用意(実際にはry)
    pFontFace->GetFiles(&numberOfFiles, NULL);   // 一応個数を確認(実際にはry)
    if (numberOfFiles == 1) {
      pFontFace->GetFiles(&numberOfFiles, pFontFiles);  // pFontFiles[0] に IDWriteFOntFile 情報へのポインタが設定される
    }

    (4) 対象フォントの 参照キー情報へのポインタを GetReferenceKey() で取得する
    const void* pFontFileReferenceKey;
    UINT32 fontFileReferenceKeySize;  // 受け取る参照キー情報のサイズ
    pFontFiles[0]->GetReferenceKey(&pFontFileReferenceKey, &fontFileReferenceKeySize);  // pFontFileReferenceKey に 参照キー情報へのポインタ、fontFileReferenceKeySize に参照キー情報のサイズが設定される

    (5) 対象フォントのローカルファイルフォントローダーへのポインタを取得する
    IDWriteFontFileLoader* pFontFileLoader;
    IDWriteLocalFontFileLoader* pLocalFontFileLoader:
    pFontFiles[0]->GetLoader(&pFontFileLoader);  // ローカルじゃないフォントファイルローダーへのポインタが pFontFileLoader に設定される
    pFontFileLoader->QueryInterface<IDWriteLocalFontFileLoader>(&pLocalFontFileLoader);  // さっき取得したフォントファイルローダーが ローカルフォントファイルローダーを実装しているなら、そこへのポインタが pLocalFontFileLoader に設定される

    (6) 対象フォントのフルパスを GetFilePathFromKey() で取得する
    WCHAR filePath[MAX_PATH];
    pLocalFontFileLoader->GetFilePathFromKey(pFontFileReferenceKey, fontFileReferenceKeySize, filePath, MAX_PATH);  // ローカルフォントファイルローダーから、参照キーに対応する対象フォントのフルパスが取得され、 filePath に設定される。フルパスの英字はすべて大文字になる模様

    なんだこのパズルみたいな手順は。

     |  Daregada  |  返信
  8. 情報ありがとうございます。

    CreateFontFaceFromHdc を使用して IDWriteFontFace を作り出す方法は海外のサイトで見かけたのですが、IDWriteFontFace からファイルのパスを取得する方法があったとは…!

    しかし IDWriteFontFace が作り出せるならファイルのパスを取得しなくても DrawGlyphRun に取得した FontFace を渡してやれば一発で解決なんじゃね?と思って試してみたら、英数字は表示できたのですが日本語が文字化けするという…

    やはりカスタムフォントコレクションを使用するしか方法はなさそうでした。

    頂いた方法でこちらでもなんとかファイルのパスを取得するところまでは行けそうなのですが、今度はそこからカスタムフォントコレクションを作るところでつまづいてます^^; 
    (MS のサンプルを Delphi に翻訳しているのですが、情報少なすぎ…)

    もう少しがんばってみます!

     |  Kuro  |  返信
  9. Delphi のバグが原因でした…
    とりあえずカスタムフォントで描画できるところまで確認できました!

    が、カスタムフォントをまじめに実装するとなるとちょと大変そう…

    以下、大雑把ですが成功した手順をメモっておきます。

    ----
    (1) 対象フォントの IDWriteFontFace 情報へのポインタを CreateFontFaceFromHdc() で取得する
    (2) 対象フォントの IDWriteFontFile 情報へのポインタを GetFiles() で取得する

    ここまでは頂いた情報の通りですが、カスタムフォントコレクションの作成にフォントファイルの絶対パスは不要でした。
    IDWriteFontCollectionLoader で使用する IDWriteFontFileEnumerator の GetCurrentFontFile というメソッドで (2) で取得した IDWriteFontFile を返すようにしてやるだけで良さそうです。

    (3) CreateCustomFontCollection でカスタムフォントコレクションを作成する
    (4) あとは IDWriteFontCollection (カスタムフォントコレクション) の GetFontFromFontFace で (1) で取得した FontFace から IDWriteFont を生成したり CreateTextFormat の引数にカスタムフォントコレクションを渡してやれば描画できました。
    ----

    カスタムフォントコレクションは IDWriteFontCollectionLoader を登録 (RegisterFontCollectionLoader) してやったり IDWriteFontFileEnumerator でごにょごにょしてやったりと扱いがやっかいな感じですが、ここさえ上手くいけば実装できるかもしれません。

     |  Kuro  |  返信
  10. > Delphi のバグが原因でした…
    > とりあえずカスタムフォントで描画できるところまで確認できました!

    おー、参考になったのであれば幸いです。処理系にバグがあるのは辛いですね...。

    > (1) 対象フォントの IDWriteFontFace 情報へのポインタを CreateFontFaceFromHdc() で取得する
    > (2) 対象フォントの IDWriteFontFile 情報へのポインタを GetFiles() で取得する

    > ここまでは頂いた情報の通りですが、カスタムフォントコレクションの作成にフォントファイルの絶対パスは不要でした。

    うん。途中でなんとなくそんな気がしてましたorz。というのも、あの手順を「すべて」使ってフルパスを取得するコードが、Google先生で検索してもひとつも出てこないので...。「フォントファイルのフルパスがほしー」って書き込みは(英文で)たくさんありましたが。チャレンジ対象としては楽しかったので、暇なときに自分のブログのネタにします。

    > カスタムフォントコレクションは IDWriteFontCollectionLoader を登録 (RegisterFontCollectionLoader) してやったり IDWriteFontFileEnumerator でごにょごにょしてやったりと扱いがやっかいな感じですが、ここさえ上手くいけば実装できるかもしれません。

    期待して待ってます。

     |  Daregada  |  返信
  11. 書き込みありがとうございます。
    いやー、ホントに助かりました。

    土日をまるまる DirectWrite に捧げてしまいましたが…w

    > うん。途中でなんとなくそんな気がしてましたorz

    "CreateCustomFontFileReference + CreateCustomFontCollection" で実装するために AddFontResource したフォントのファイルのフルパスが取れれば~と思っていたので、遠回りになってしまいました^^;

    結局のところ、最初に Daregada さんがおっしゃっていた「GDI のシステムフォントコレクションにあって、DirectWrite のシステムフォントコレクションにないフォント」の一覧をもとにカスタムフォントコレクションを作ってやる形になりそうです。(単純に比較できないし、インストールされているフォントの数によっては結構遅くなりそうなので厄介ですが…)

    AddFontResource したフォントだけの一覧を取得できるような便利な方法があれば一発なのですが…

     |  Kuro  |  返信
  12. おつかれさまです。

    > 土日をまるまる DirectWrite に捧げてしまいましたが…w

    なんかすいません。

    こちらは土日でMeryのマクロ(学生向け)を書きまくってました。編集中のHTMLファイルの内容をvalidator.nuに送信して検証結果を得るマクロとか、同様のCSSファイルの検証マクロとか、noonworksさんのHDADailog.jsを使って、CSSのカラーネーム一覧ダイアログを表示し、取得したカラーネームをカーソル位置に挿入するマクロとか、終了タグを自動補完するマクロとか...。

    まあ学生のためというより、「青を brue と書いて色が変わりません!! とか言う学生に対応してられねぇよ」って自分のためです。落ち着いたらMeryWikiに公開しようと思っています。

    > 結局のところ、最初に Daregada さんがおっしゃっていた「GDI のシステムフォントコレクションにあって、DirectWrite のシステムフォントコレクションにないフォント」の一覧をもとにカスタムフォントコレクションを作ってやる形になりそうです。(単純に比較できないし、インストールされているフォントの数によっては結構遅くなりそうなので厄介ですが…)

    > AddFontResource したフォントだけの一覧を取得できるような便利な方法があれば一発なのですが…

    実装を見てないので適当なことを言いますが、AddFontResource()したフォントすべてを一度に取得する必要は無いのでは。

    Mery 2系だと、オプションダイアログの[表示]-[フォント]で開くダイアログで、GDIのシステムフォントコレクション(AddFontResource()したフォントを含む)が一覧表示されますよね。現状は、ここで「AddFontResource()したフォントが選択された場合、DirectWriteが有効な設定なら、CreateFontFromLOGFONT()でIDWriteFont情報に変換を試み、失敗するのでMeiryoを代わりに使う」ようです。

    この部分を、「CreateFontFromLOGFONT()が失敗した場合、ここで判明した方法を使って得たフォント情報を追加したカスタムフォントコレクションを作ってそれを利用する、作成に失敗したらMeiryoを代わりに使う」みたいにできれば、ダイアログでユーザーが選んだフォントだけ処理すればいいですよね。
    あとは、次回以降のMeryの起動時にフォントを設定するときにも、同じような処理が必要ですね。

     |  Daregada  |  返信
  13. おつかれさまです。ご返信ありがとうございます。

    > なんかすいません。

    いえいえ、そもそも趣味の日曜プログラミングで開発しているソフトですのでモチベーションがあがりそうなネタは大歓迎です。今回の敵はかなり手強かったので楽しめましたw

    > 落ち着いたらMeryWikiに公開しようと思っています。

    面白そうなマクロですね。是非よろしくお願いいたします!
    次のバージョンの Mery では Tidy を HTML5 対応版に差し替えており、HTML の検証もできるようになっているのでお役に立てるかもしれません。(validator.nu のほうが精度高いかもですが)

    > 現状は、ここで「AddFontResource()したフォントが選択された場合、DirectWriteが有効な設定なら、CreateFontFromLOGFONT()でIDWriteFont情報に変換を試み、失敗するのでMeiryoを代わりに使う」ようです。

    おっしゃる通りの仕様です。

    > 「CreateFontFromLOGFONT()が失敗した場合、ここで判明した方法を使って得たフォント情報を追加したカスタムフォントコレクションを作ってそれを利用する

    実装に関しては色々とご報告を省略してしまいましたが、実は↑この方法は最初に試しました…

    AddFontResource でフォントが 1 つだけ追加されている場合なら問題ないと思いますが、複数のフォントが AddFontResource で追加されている状況では、カスタムフォントコレクションの仕様において、下記の条件との兼ね合いで問題がありそうでした。

    まず、フォントを切り替えるたびにカスタムフォントコレクションを作成する方式の場合。

    ①カスタムフォントコレクションの作成 (CreateCustomFontCollection) にはアプリケーションが定義した固有のキー (ファクトリを使用してローダーの登録が解除されるまで有効な固定キー。普通は定数) が必要で、一度作成したカスタムフォントコレクションは破棄することができない

    ※CreateCustomFontCollection の引数に一度使用したことのあるキーを渡すと、以前に使用していたカスタムフォントコレクションが返ってくるので、内部でキャッシュみたいに?保持してるっぽいです

    →この仕様を考慮すると、カスタムフォントコレクションっていくつでも作って OK というものではなく、アプリケーション内で固定数 (普通は 1 つ、または数えられる程度) のみ生成して使うことが想定されているのだと思います。

    そこで、カスタムフォントコレクションは 1 つだけ用意して、必要に応じてそのカスタムフォントコレクションに追加してやればいいんじゃね?と思いましたが、下記の仕様がありました。

    ②カスタムフォントコレクションは作成時 (CreateCustomFontCollection を呼んだ時) に一度だけ、追加するフォントをまとめて読み込み、一度作成したコレクションに後からフォント情報を追加することはできない

    →このため、カスタムフォントコレクションをあらかじめ作成しておき、CreateFontFromLOGFONT() が失敗した時にだけコレクションに追加して使う、といったことができません。

    これらを踏まえると、例えば AddFontResource で複数のフォントを追加している場合、それらのフォントを切り替えるたびに固有のキーを生成してやってそれをもとに新たなカスタムフォントコレクションを作るという方法は物理的には可能ですが、アプリケーション側で生成した固有キーをすべて保持しておく必要がある点と、内部でカスタムフォントコレクションのキャッシュみたいなもの?が溜まっていってそうな点において、精神衛生上よろしくないかなと思いました。

    カスタムフォントコレクションの仕様に従って妥当な使い方をするなら、CreateFontFromLOGFONT() が失敗した時に一度だけカスタムフォントコレクションを作成。この時に他の追加フォントもまとめてカスタムフォントコレクションに登録しておいて、フォント切り替え時に再び CreateFontFromLOGFONT() が失敗した場合はそのカスタムフォントコレクションを使う、といった小細工が必要かなと思いました。

    ただ、この方法だと他の追加フォントもまとめて登録するところがボトルネックです。
    GDI のフォント一覧をくるくる回して CreateFontFromLOGFONT() が失敗するフォントで、なおかつ CreateFontFaceFromHdc() が成功するフォントを抽出するという方法で実装してみていますが、速度的には許容範囲なものの、インストールされているフォントの数によっては遅くなる恐れがありますので。

    長くなってしまいましたが、現状はこんなところです。
    何かもっと素敵な方法ないかしら…

     |  Kuro  |  返信
  14. > 何かもっと素敵な方法ないかしら…

    追伸:
    先ほど、風呂に入っていて思いついたのですが、一度、DWriteFactory の「UnregisterFontCollectionLoader()」してやって再び「RegisterFontCollectionLoader()」し直してやれば同じ固有キーでフォントコレクションを作れるかも!

    と思ったのですが、DWriteFactory がシングルトンなので、エディタウィンドウでフォントを使っている最中にフォント設定画面で別のフォントをプレビューしたときにも UnregisterFontCollectionLoader されちゃって、エディタウィンドウで使われているフォントが使用できなくなっちゃうかなぁ…

    ふむぅ。

     |  Kuro  |  返信
  15. なるほど、そんな制限があったとは。失礼しました。

    > > 何かもっと素敵な方法ないかしら…

    > 追伸:
    > 先ほど、風呂に入っていて思いついたのですが、

    ファミレスのトイレ個室に入っていて思いついたのですが、GDIシステムフォントコレクションの大半は、普通にインストール済みですから、例のレジストリと付き合わせれば、いちいちCreateFontFromLOGFONTしないでも対象から取り除けませんかね。

    残ったフォント達だけくるくるすれば、そんなに重い処理にはならないんじゃないかな。何百個もAddFontResourceしてる人がいなければ...そんなのテストしたくない。

    あとは、AddFontResourceしたと思しきフォント名のリストを、こそっとファイルにキャッシュしちゃうとか。システムフォントコレクションに変更がなければ、リストがそのまま使えるはず。

     |  Daregada  |  返信
  16. ご返信ありがとうございます。

    > 例のレジストリ

    調べてみましたところ、例のレジストリは英語のフォント名が格納されており、GDI のフォント一覧 (EnumFontFamiliesEx) はシステムロケールに応じたフォント名 (日本語) が返ってくるだけ。英語のフォント名の一覧を取得するには DWrite の GetSystemFontCollection を使えと…(ぉ

    一応、もしフォント一覧が取得できたと想定して、300 個ぐらいのフォントで速度を比較してみましたが、リスト同士を比較して差分を探すより、1 つのリストですべてのフォントを CreateFontFromLOGFONT して失敗するものを抽出したほうが高速でしたw

    CreateFontFromLOGFONT って内部でフラグか何かで判断しているのでしょう、意外と速いです。CreateFontFromLOGFONT で失敗するものだけを抽出すれば、ほぼ AddFontResource されたフォントの一覧に近いものが作れますが、今度は CreateFontFaceFromHdc が遅いっていうオチでしたorz

    AddFontResource されたフォントが 100 以上とかあると体感的に分かるレベルで遅くなります。(そんなケース考えたくありませんがw)

    > あとは、AddFontResourceしたと思しきフォント名のリストを、こそっとファイルにキャッシュしちゃうとか。システムフォントコレクションに変更がなければ、リストがそのまま使えるはず。

    Chromium 関連ですが、カスタムフォントコレクションのキャッシュを作る仕様を検討しているフォーラムを見つけましたが、AddFontResource したフォントを使うというちょっとレアなケースへの対応ということを考慮すると、そこまで本格的に実装するのも気が引けますね^^;

    とりあえず現状で Mery に組み込んでみたところ、カスタムフォントコレクションの作成自体は一瞬 (体感的には分からないレベル) ですが、どうもカスタムフォントコレクションを使うと DWrite の文字描画自体が遅いようで、グラボの性能にも依存するかもしれませんが、ギリギリ実用に耐えるかどうかというレベルでした。

    もう少し調査して、速度的に納得できるレベルのものができそうであれば実装してみたいと思います。

     |  Kuro  |  返信
スポンサーリンク