【问题标题】:How to find the EXACT pixel height of text rendered in Windows using DrawText如何使用 DrawText 查找在 Windows 中呈现的文本的精确像素高度
【发布时间】:2015-05-19 23:42:42
【问题描述】:

我希望找到在 Windows 中呈现的文本的确切高度。我已经尝试过 GetTextExtentPoint32 和使用 DT_CALCRECT 标志调用 DrawText 并且都给出了相同的结果。

似乎返回的高度是基于整个单元格的高度,而不考虑要绘制的实际文本。

下面的代码是标准 Visual Studio 2013 Win32 项目的 WM_PAINT 处理程序。它创建一个(大)字体并绘制示例文本。文字最高部分为98像素,但GetTextExtentPoint32返回的值为131。

我意识到有些应用程序可能需要完整的单元格高度,但也有些应用程序(比如我的)只需要文本使用的 实际 高度。

有人知道如何找到这些信息吗?

是的,我可以渲染到内存 DC 并向下扫描以寻找第一个非背景颜色的像素——但这会非常慢。

谢谢

case WM_PAINT:
{
    hdc = BeginPaint (hWnd, &ps);

    HFONT hfont = CreateFont (-99, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, VARIABLE_PITCH, L"Segoe UI Semibold");
    auto old_hfont = SelectObject (hdc, hfont);

    wchar_t sample_text[] = L"123 Testing 123";
    size_t sample_text_length = wcslen (sample_text);

    SIZE s;
    GetTextExtentPoint32 (hdc, sample_text, sample_text_length, &s);

    RECT r = {10, 10, 10 + s.cx, 10 + s.cy};

    SetBkColor   (hdc, RGB (80, 120, 160));
    SetTextColor (hdc, RGB (220, 220, 220));

    DrawText (hdc, sample_text, sample_text_length, &r, DT_SINGLELINE | DT_NOPREFIX | DT_LEFT | DT_TOP);

    SelectObject (hdc, old_hfont);
    DeleteObject (hfont);

    EndPaint (hWnd, &ps);
    break;
}

【问题讨论】:

  • 在抗锯齿字体上,您的确切高度是多少?
  • DrawText 操作写入的光栅线总数?我的意思是写入的第一条和最后一条光栅线之间的高度
  • 出于好奇,您能否提供有关此用例的更多详细信息? My guy instinct says that there may be a better way to solve your actual problem.
  • 在尝试解决这些问题时,您会发现一件事是,您正在编写的代码表面上会执行所有所需的逻辑,以使用所有字距调整和偏移以及其他排版魔法(包括在希伯来语和其他语言中发现的所有具有重叠字符的魔法)。你会用你的一生来努力把它做好。我建议只使用 DT_CALCRECT。你的结果将是一致的,周围有干净的填充,它是一行代码而不是数千行。
  • 对于像 Anthony 和 Marc 这样的名字,我猜您希望摆脱英文文本上方看似很大的空白。保留用于重音和变音符号,(几乎)在英文文本中不存在。在示例代码中它占用了 32 个像素,非常明显。只需从 Y 位置减去 TEXTMETRICS.tmInternalLeading。小心不要在上面涂上任何东西。

标签: c++ windows gdi


【解决方案1】:

看看GetGlyphOutline(GGO_METRICS) 返回的GLYPHMETRICS 结构应该包含计算范围所需的所有数据。

【讨论】:

    【解决方案2】:

    您的文本不是直接绘制的,首先他变成了path,它描述了您的几何图形/字形的轮廓。路径由移动、直线和曲线(以及之前的关闭标志)组成。除曲线外,其他路径段是最终填充的极值点。将曲线转换为直线并遍历所有路径段以通过点找到水平和垂直维度的最小值和最大值,从而得到最接近文本的矩形。

    您可以通过在之前调用BeginPath 和之后调用EndPath 来绘制他来将文本转换为路径。 FlattenPath 进行曲线->直线转换。 GetPath 提供对上下文路径点的访问。 AbortPath 最终将路径从上下文中移除。

    当您没有set the background to transparent 时,路径可能是文本周围的背景,并且第一个路径段已经是背景/范围矩形线 - 不是您想要的。

    为简化此方法,您可以通过 1) 低于基线 2) 高于中线 3) 其余部分排除重复字符和分组字符。与您的“外观像素”尝试相比,这一切都运行得非常快。

    关于文本大小的其他有用来源是 font metrics (GetTextMetrics) 和 character widths (GetCharABCWidths)

    【讨论】:

      【解决方案3】:

      如果您的示例文本仅包含 ASCII 字母,您可以手动评估升序字母(如 b)、降序字母(如 g)和中值字母(如 x)的高度。你会提前做一次,甚至可能离线,所以任何低效的方法都很好。

      然后(取决于字体,误差范围很小)计算总高度是对字符串中的上升(bdfhijkltA-Z)和下降(gjpqy)的简单检查。

      【讨论】:

      • 但这就是问题所在 - 当 Windows API 将所有字符视为具有相同高度时,您如何“手动评估字母的高度”?
      • @MarkRansom 正如原帖中提到的那样,扫描效率低下。三个高度将被预先计算一次,甚至可能离线,因此效率低下并没有什么坏处。
      【解决方案4】:

      如果这是 TrueType 字体,您可能会发现字形定位的大小 w.r.t。如here 所述,fmtx 表中的“上一个”字母和行。还有 similar information 可用于 OpenType 字体,但派生一些有意义的东西看起来更麻烦。

      这不会为您提供字形的实际像素大小,但它确实可以让您更准确地了解特定字形的真实字体路径相对于“基线”和单位的绘制位置随点大小缩放并且是全字体的。

      在您的情况下,我会看看 FreeType,它是一个快速并支持光栅字体、TrueType 和 OpenType 字体的第 3 方库。它被广泛使用并且非常well documented 和支持并且可以提供准确的指标。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-06-25
        • 2020-08-18
        • 2011-12-17
        • 1970-01-01
        • 1970-01-01
        • 2018-02-05
        • 1970-01-01
        • 2023-03-30
        相关资源
        最近更新 更多