10

私の目的: 特定の高さの IDWriteTextLayout に収まるテキストの行数を計算できるように、IDWriteTextFormat のフォントの高さを取得したいと考えています。

私の問題: 現在、このコードを使用して表示可能な行数を計算しています:

inline int kmTextCtrl::GetVisLines() const
{

    /* pTextFormat is an IDWriteTextFormat pointer, dpi_y is the desktop's vertical dpi,
       and GetHeight() returns the height (in pixels) of the render target. */
    float size = (pTextFormat->GetFontSize()/72.0f)*dpi_y;
    return (int)(GetHeight()/size);
}

計算は一部のフォントでは正確であるように見えますが、TrueType フォント (例: Courier New、Arial、Times New Roman) では正確ではありません。これらのフォントの場合、表示されるテキストは、レンダー ターゲットの垂直方向の境界線よりもかなり手前で切り取られます。

いくつかのコンテキスト: IDWriteTextLayout を使用してテキストをコントロールのレンダー ターゲットに配置するテキスト スクロール バック バッファー コントロールを作成しています。GetVisLines() の結果を使用して、(行ごとに std::strings にテキストを格納する) 循環バッファーから何行のテキストをレイアウトに取り込み、ウィンドウがスクロールまたはサイズ変更されるたびに再作成するかを決定します。

これは、「ネイティブ」Win32 API C++ を使用して行われています。

4

2 に答える 2

11

最も単純で最も堅牢なアプローチは、レイアウト自体にテキスト メトリックを要求することです。これは、描画と測定のために設計された 2 つのことの 1 つです。IDWriteTextLayoutテキスト形式を使用してを作成し、を呼び出しGetMetricsて を取得しDWRITE_TEXT_METRICS::heightます。テキスト形式を使用ID2D1RenderTarget::DrawTextして渡していると思うので、レイアウトを直接作成していない可能性がありますが、呼び出しDrawTextCreateTextLayout自分自身を呼び出してからDrawTextLayout.

この回答 (IDWriteFontFaceなど) を取得するために下位層を通過すると、基本フォントが使用され、すべての行が同じ高さであると仮定するなど、一般的な世界対応のテキスト コントロールが想定すべきではない特定の想定が行われることに注意してください。すべての文字が指定されたベース フォントに存在する限り、これはたまたまうまくいきます (ほとんどの場合、英語を表示している可能性が高いため、すべてが適切に表示されます)。ただし、いくつかの CJK または RTL 言語または絵文字 (これらはベース フォントです) Times New Roman のように、確かにサポートしていません)、行の高さは、置換されたフォントに応じて増減します。GDI は、ベース フォントの高さに収まるように代替フォントを再スケーリングしますが、これにより、アセンダーとディセンダーのためにより多くの呼吸の余地が必要なタイ語やチベット語などの言語では、文字の圧縮が不十分になります。IDWriteTextLayoutまた、WPF/Word のレイアウトのような他のレイアウトでは、すべてのフォント グリフが同じ em サイズに保たれます。ただし、行の高さが可変であることを意味します。

テキストの各行をすべて同じ高さであるかのように描画すると、グリフ間のオーバーラップや行間の不均一なベースライン、またはコントロールの上部と下部でのクリッピングが見られます。したがって、理想的な方法は、各行の実際の高さを使用することです。ただし、それらをすべて同じ高さにする必要がある場合 (またはコントロールが複雑すぎる場合) は、少なくとも with を使用SetLineSpacingして明示的な行間隔をDWRITE_LINE_SPACING_UNIFORMベース フォントの行間隔に設定します。これにより、ベースラインの間隔が均一になります。

興味深いことに、IDWriteTextLayout は、その行のすべてのランの高さの最大値として行の高さを計算し、1 つのランの高さ (同じフォントと em サイズ) は、デザイン メトリック (アセント + ディセント、および発生する lineGap) を使用するだけです。存在しないようにします (ほとんどのフォントはこれをゼロに設定しますが、Gabriola は大きな行間ギャップの良い例です)。すべての em サイズは、ポイント (1/72 インチ) ではなく、DIP (通常の 96DPI では 1:1 を意味し、DIP は正確には == ピクセル) であることに注意してください。

(ascent + descent + lineGap) * emSize / designUnitsPerEm

于 2011-09-10T07:40:14.943 に答える
7

私は答えを見つけました。Directwrite で行の間隔 (フォントの高さと間隔) を見つけるには、次のようなことを行う必要があります。

inline int kmTextCtrl::GetVisLines() const
{

    IDWriteFontCollection* collection;
    TCHAR name[64]; UINT32 findex; BOOL exists;
    pTextFormat->GetFontFamilyName(name, 64);
    pTextFormat->GetFontCollection(&collection);
    collection->FindFamilyName(name, &findex, &exists); 
    IDWriteFontFamily *ffamily;
    collection->GetFontFamily(findex, &ffamily);
    IDWriteFont* font;
    ffamily->GetFirstMatchingFont(pTextFormat->GetFontWeight(), pTextFormat->GetFontStretch(), pTextFormat->GetFontStyle(), &font);
    DWRITE_FONT_METRICS metrics;
    font->GetMetrics(&metrics);
    float ratio = pTextFormat->GetFontSize() / (float)metrics.designUnitsPerEm;
    float size = (metrics.ascent + metrics.descent + metrics.lineGap) * ratio;
    float height = GetHeight();
    int retval = static_cast<int>(height/size);
    ffamily->Release();
    collection->Release();
    font->Release();
    return retval;
}

もちろん、頻繁に使用されるインライン関数を呼び出さなければならないたびに、そのすべてを実行したくはないでしょう。

于 2011-04-10T06:23:04.647 に答える