计算系统字体的 DIPs 中的字体 "size"

Calculate font "size" in DIPs of a system font

我有几个 BS_OWNERDRAWN 按钮并使用 Direct2D 和 Direct Write 来绘制它们。

我还需要在按钮矩形内绘制按钮文本,为此我使用 IDWriteTextFormat,这需要在 DIP(设备独立像素)中指定“字体大小”。

我希望这些按钮中的字体大小与其他非所有者绘制的公共控件的大小相同,或者与 window 标题栏中的系统字体大小相同。

以下代码是“截断”版本,用于展示我的解决方法,当然不会给出预期结果,因为我在标题栏中得到 LOGFONT 字体结构,它给出了字体的宽度(单个字符)但不是 IDWriteTextFormat 期望在 DIP 中指定字体大小的字体大小。

class CustomControl
{
protected:
    /** Caption text format used to draw text */
    CComPtr<IDWriteTextFormat> mpTextFormat;

    /** Caption font size (button text size) */
    float mFontSize;
};

// Calculate caption bar (default) font size of a top level window
void CustomControl::CalculateFontSize()
{
    NONCLIENTMETRICSW metrics;
    metrics.cbSize = sizeof(NONCLIENTMETRICSW);
    SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0);

    LOGFONTW font = metrics.lfCaptionFont;
    mFontSize = static_cast<float>(font.lfHeight);
}

// Create text format that is of same font size as default system font
HRESULT CustomControl::CreateTextFormat()
{
    HRESULT hr = S_OK;

    if (!mpTextFormat)
    {
        CalculateFontSize();

        hr = mpWriteFactory->CreateTextFormat(
            L"Arial",
            NULL,
            DWRITE_FONT_WEIGHT_NORMAL,
            DWRITE_FONT_STYLE_NORMAL,
            DWRITE_FONT_STRETCH_NORMAL,
            mFontSize,  // <-- Specifies font size in DIP's..
            L"en-us",
            &mpTextFormat);
    }

    return hr;
}

这是一个测试程序,它显示了 window 标题中的默认系统字体与下面的自定义按钮文本之间的字体大小差异。

我需要帮助来弄清楚如何正确计算 IDWriteTextFormat 参数的字体大小,使其与其他非 BS_OWNERDRAW 的常用控件中的文本或本示例中的默认字体具有相同的大小window 标题。

编辑:

我发现问题出在我的 CalculateFontSize() 我写了 mFontSize = static_cast<float>(font.lfHeight); 但这是负数所以附加 - 符号给出了预期的结果:

mFontSize = static_cast<float>(-font.lfHeight);

为什么是负面的?我还不确定,但这个答案有帮助:

How to set font size using CreateFontA?

现在我的问题仍然是我应该如何更新 CalculateFontSize() 以便它获得不是 BS_OWNERDRAW 的常用控件的字体大小而不是 window 标题栏字体大小?

我想出了计算要用于 IDWriteTextFormat 的其他常用控件的字体大小的公式很简单:

float CustomControl::CalculateFontSize()
{
    const long units = GetDialogBaseUnits();
    const DWORD height = HIWORD(units);
    return static_cast<float>(height);
}

唯一的问题是,如果您为常用控件使用自定义字体,或者如果您的对话框使用不同的字体,那么您需要更新您的 CalculateFontSize() 以将这些更改考虑在内。

然而,为了让您的 TextFormat 与原生通用控件真正保持一致,您还需要应用字体粗细(粗体),例如在您创建 TextLayout 之后(通过使用您的 TextFormat):

    std::size_t caption_len = 0;
    StringCchLengthW(mCaption, STRSAFE_MAX_CCH, &caption_len);

    DWRITE_TEXT_RANGE range = { 0u, caption_len };

    mpTextLayout->SetFontSize(mFontSize, range);
    mpTextLayout->SetFontWeight(DWRITE_FONT_WEIGHT::DWRITE_FONT_WEIGHT_BOLD, range);