直接写:获得字体的高度

时间:2011-04-08 02:52:21

标签: c++ winapi native direct2d directwrite

我的目标:   我想获得IDWriteTextFormat字体的高度,这样我就可以计算出在一定高度的IDWriteTextLayout中可以容纳多少行文本。

我的问题:   现在我正在使用此代码计算可见的行数:

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 ++完成的。

2 个答案:

答案 0 :(得分:8)

最简单,最强大的方法是只询问布局本身的文本指标,因为这是它为设计,绘图和测量设计的两件事之一。您可以使用文字格式创建IDWriteTextLayout并致电GetMetrics以获取DWRITE_TEXT_METRICS::height。我猜你正在使用ID2D1RenderTarget::DrawText并传递文字格式,因此你可能没有直接创建布局,但调用DrawText就像调用CreateTextLayout自己跟着{{ 1}}。

请注意,通过较低层来获得此答案(DrawTextLayout之类)会做出某些假设,即通用世界就绪文本控件不应该假设,例如假设将使用基本字体并且所有线条高度相同。只要所有字符都出现在给定的基本字体中,这就好了(很可能你大部分都显示英语,这就是为什么一切都很好),但是输入一些CJK或RTL语言(像Times这样的基本字体) New Roman当然不支持),并且行高将相应地增加或缩小到替换字体。 GDI重新调整替换字体,使它们符合基本字体的高度,但这会导致泰语和藏语等语言中的字母很差,需要更多的呼吸空间来上升和下降。 IDWriteFontFace和WPF / Word中的其他布局将所有字体字形保持在相同的em大小,这意味着它们在彼此相邻时排列得更好;但这确实意味着线高是可变的。

如果您只是绘制每行文本,就好像它们都是相同的高度一样,您可以看到字形之间的字形和非均匀基线之间的重叠,或者在控件的顶部和底部进行剪裁。所以理想的做法是使用每条线的实际高度;但是如果你需要它们都是相同的高度(或者如果它使控件太复杂),那么至少使用IDWriteTextLayoutSetLineSpacing设置一个明确的行间距到基本字体的那个 - 基线均匀间隔的方式。

虽然,好奇,是的,行高确实是使用字体设计指标ascent + descent确定的,加上恰好存在的任何lineGap(大多数字体将此设置为零,但Gabriola是一个很好的例子线间隙),乘以em大小并除以每个em的单位。注意所有的em尺寸都是DIP(典型的96DPI表示1:1,DIP正好==像素),而不是点(1/72英寸)。

答案 1 :(得分:7)

我找到了answer。 要在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;
}

当然,您可能不希望每次必须调用常用的内联函数时都这样做。