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++ 完成的。
My objective:
I want to get the height of an IDWriteTextFormat's font so I can calculate how many lines of text can fit in an IDWriteTextLayout of a certain height.
My problem:
Right now I'm using this code to calculate the visible number of lines:
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);
}
The calculation seems to be accurate for some fonts, but not for any of the TrueType fonts (e.g.: Courier New, Arial, Times New Roman). For these fonts, the text shown is clipped well short of the lower vertical boundary of the render target.
Some context:
I am making a text scroll back buffer control which uses an IDWriteTextLayout to put text to the control's render target. I use the result of GetVisLines() to determine how many lines of text from a circular buffer (which stores text in std::strings by the line) to pull into the layout, and recreate it every time the window is scrolled or resized.
This is being done using "native" Win32 API C++.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
最简单、最可靠的方法是只向布局本身询问文本指标,因为这是它设计的两件事之一:绘图和测量。您可以使用文本格式创建一个
IDWriteTextLayout
并调用GetMetrics
来获取DWRITE_TEXT_METRICS::height
。我猜您正在使用ID2D1RenderTarget::DrawText
并传递文本格式,因此您可能没有直接创建布局,但调用DrawText
就像调用 < code>CreateTextLayout 自己,然后是DrawTextLayout
。请注意,通过较低层来获取此答案(
IDWriteFontFace
等)会做出通用的世界就绪文本控件不应假设的某些假设,例如假设将使用基本字体并且所有线条高度相同。只要所有字符都以给定的基本字体出现,就会出现这种情况(您可能主要显示英语,这就是为什么所有字符都显示良好),但添加一些 CJK 或 RTL 语言或表情符号(这是一种基本字体)像Times New Roman肯定不支持),行高会根据替换的字体相应增大或缩小。 GDI 会重新调整替换字体的大小,使其适合基本字体的高度,但这会导致泰语和藏语等语言中的字母变得很糟糕,这些语言需要更多的喘息空间来供上升和下降。 IDWriteTextLayout 和其他布局(例如 WPF/Word 中的布局)将所有字体字形保持相同的 em 大小,这意味着它们在彼此相邻时排列得更好;但这确实意味着行高是可变的。如果您只是绘制每一行文本,就好像它们都具有相同的高度一样,您可以看到字形之间的重叠和行之间基线的不均匀,或者在控件的顶部和底部进行剪裁。所以理想的做法是使用每行的实际高度;但如果您需要它们都具有相同的高度(或者如果它使控件过于复杂),那么至少使用
SetLineSpacing
和DWRITE_LINE_SPACING_UNIFORM
设置显式行间距基本字体的 - 这样基线的间距是均匀的。尽管出于好奇,IDWriteTextLayout 将行高计算为该行上所有运行高度的最大值,并且单个运行的高度(相同的字体和 em 大小)仅使用设计指标:上升 + 下降,加上发生的任何 lineGap存在(大多数字体将其设置为零,但 Gabriola 是大行间隙的一个很好的例子)。请注意,所有 em 尺寸均以 DIP 为单位(典型的 96DPI 表示 1:1,DIP 恰好 == 像素),而不是点(1/72 英寸)。
(上升 + 下降 + lineGap) * emSize / designUnitsPerEm
The simplest and most robust approach is to just ask the layout itself for text metrics, as that's one of the two things it was designed for, drawing and measurement. You would create an
IDWriteTextLayout
using the text format and callGetMetrics
to get theDWRITE_TEXT_METRICS::height
. I'm guessing you're usingID2D1RenderTarget::DrawText
and passing a text format, so you may not have created a layout directly, but callingDrawText
is just like callingCreateTextLayout
yourself followed byDrawTextLayout
.Beware that going through the lower layers to get this answer (
IDWriteFontFace
and the like) makes certain assumptions that a generic world ready text control should not assume, such as assuming the base font will be used and that all the lines being the same height. So long as all characters are present in the given base font, this happens to work out (chances are you're mostly displaying English which is why all appears well), but throw in some CJK or RTL languages or emoji (which a base font like Times New Roman certainly doesn't support), and the line height will grow or shrink accordingly to the substituted fonts. GDI rescales substituted fonts such that they fit into the base font's height, but this leads to poorly scrunched letters in languages like Thai and Tibetan which need more breathing room for ascenders and descenders.IDWriteTextLayout
and other layouts like those in WPF/Word keep all the font glyphs at the same em size, which means they line up more nicely when adjacent to each other; but it does mean the line height is variable.If you do just draw each line of text as if they were all the same height, you can see overlap between glyphs and non-uniform baselines between lines, or clipping at the top and bottom of the control. So the ideal thing to do is to use the actual height of each line; but if you need them to all be the same height (or if it complicates the control too much), then at least set an explicit line spacing using
SetLineSpacing
withDWRITE_LINE_SPACING_UNIFORM
to that of the base font - that way the baselines are uniformly spaced.Though for the curious, IDWriteTextLayout computes the line height as the maximum of all run heights on that line, and the height of a single run (same font and em size) just uses the design metrics: ascent + descent, plus any lineGap that happens to be present (most fonts set this to zero, but Gabriola is a good example of large line gap). Note all em sizes are in DIP's (which at typical 96DPI means 1:1, DIP's exactly == pixels), not points (1/72 inch).
(ascent + descent + lineGap) * emSize / designUnitsPerEm
我找到了答案。
要在 Directwrite 中查找行间距(字体高度加间隙),您必须执行类似于以下操作的操作:
当然,您可能不想每次调用常用内联函数时都执行所有这些操作。
I have found an answer.
To find the spacing of a line (font height plus gap) in Directwrite, you must do something akin to the following:
Of course, you probably don't want to do all that every time you have to call a frequently-used inline function.