直接写:获得字体的高度

kit*_*tty 7 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);
}
Run Code Online (Sandbox Code Playgroud)

对于某些字体,计算似乎是准确的,但对于任何TrueType字体都不准确(例如:Courier New,Arial,Times New Roman).对于这些字体,显示的文本剪裁得远远低于渲染目标的下垂直边界.

一些上下文: 我正在制作一个文本回滚缓冲区控件,它使用IDWriteTextLayout将文本放到控件的渲染目标中.我使用GetVisLines()的结果来确定循环缓冲区(由行存储std :: strings中的文本)中的文本行数以拉入布局,并在每次滚动或调整窗口大小时重新创建它.

这是使用"本机"Win32 API C++完成的.

Dwa*_*son 9

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

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

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

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


kit*_*tty 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;
}
Run Code Online (Sandbox Code Playgroud)

当然,您可能不希望每次必须调用常用的内联函数时都执行所有操作.