我的目标: 我想获得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 ++完成的。
答案 0 :(得分:8)
最简单,最强大的方法是只询问布局本身的文本指标,因为这是它为设计,绘图和测量设计的两件事之一。您可以使用文字格式创建IDWriteTextLayout
并致电GetMetrics
以获取DWRITE_TEXT_METRICS::height
。我猜你正在使用ID2D1RenderTarget::DrawText
并传递文字格式,因此你可能没有直接创建布局,但调用DrawText
就像调用CreateTextLayout
自己跟着{{ 1}}。
请注意,通过较低层来获得此答案(DrawTextLayout
之类)会做出某些假设,即通用世界就绪文本控件不应该假设,例如假设将使用基本字体并且所有线条高度相同。只要所有字符都出现在给定的基本字体中,这就好了(很可能你大部分都显示英语,这就是为什么一切都很好),但是输入一些CJK或RTL语言(像Times这样的基本字体) New Roman当然不支持),并且行高将相应地增加或缩小到替换字体。 GDI重新调整替换字体,使它们符合基本字体的高度,但这会导致泰语和藏语等语言中的字母很差,需要更多的呼吸空间来上升和下降。 IDWriteFontFace
和WPF / Word中的其他布局将所有字体字形保持在相同的em大小,这意味着它们在彼此相邻时排列得更好;但这确实意味着线高是可变的。
如果您只是绘制每行文本,就好像它们都是相同的高度一样,您可以看到字形之间的字形和非均匀基线之间的重叠,或者在控件的顶部和底部进行剪裁。所以理想的做法是使用每条线的实际高度;但是如果你需要它们都是相同的高度(或者如果它使控件太复杂),那么至少使用IDWriteTextLayout
和SetLineSpacing
设置一个明确的行间距到基本字体的那个 - 基线均匀间隔的方式。
虽然,好奇,是的,行高确实是使用字体设计指标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;
}
当然,您可能不希望每次必须调用常用的内联函数时都这样做。