GDIVisual C++ 8.0Visual C++ 7.0Windows 2003Windows 2000Visual C++ 6.0Windows XPXML中级开发Visual StudioWindowsC++
斜体字体的文本宽度






4.80/5 (24投票s)
展示了 GetCharABCWidths 和 GetCharABCWidthsFloat 的替代方法
引言
我们的目的是准确测量斜体文本的宽度。
Using the Code
GetTextExtentPoint32
Win32 API 函数或带有 DT_CALCRECT
标志的 DrawText
Win32 API 函数,并*不能*告诉我们斜体文本的正确宽度,只有计算出的高度是正确的。对于大多数斜体字体,使用这两个函数计算出的宽度太窄,显示的文本看起来被右侧截断了。
我们可以尝试使用 GetCharABCWidths
或 GetCharABCWidthsFloat
Win32 API 函数。这些函数将为我们提供有关我们感兴趣的文本中每个字符的下伸和上伸的信息。
请查看这些 MSDN 文章,了解有关使用下伸和上伸值的更多详细信息
- 信息:计算粗体和斜体文本的范围
- HOWTO:混合斜体和常规(非斜体)文本(下载压缩的旧 Microsoft KB 文章)
- 如何将文本放入矩形中
- 信息:使用 GetCharABCWidths() 计算文本范围
- 字符宽度
此 EXE 的更新版本包含一个新的复选框,用于查看添加“最后一个字符的上伸”如何有助于获得更准确的结果。
但是,在本文中,我建议另一种方法
- 在填充了白色的内存设备上下文中,以黑色绘制文本,如下所示
SIZE sizeText; //Calculate the width of the text, by using the classic method GetTextExtentPoint32(hDCMem,szText,lstrlen(szText),&sizeText); //Calculate the width of the last character, as suggested by 'oupoi' SIZE sizeLastCharacter; GetTextExtentPoint32(hDCMem,&szText[-1+lstrlen(szText)],1,&sizeLastCharacter); //Set a bounding rectangle wide enough to fit the painted text RECT rect={0,0,sizeText.cx+sizeLastCharacter.cx,sizeText.cy}; //Fill the background with white colour then paint the text in black colour FillRect(hDCMem,&rect,(HBRUSH)GetStockObject(WHITE_BRUSH)); DrawText(hDCMem,szText,-1,&rect, DT_LEFT|DT_TOP|DT_SINGLELINE|DT_NOPREFIX);
- 然后,从右到左扫描内存设备上下文中像素的颜色,如下所示
int iXmax=0; BOOL bFound=FALSE; for(int x=rect.right-1; x>=0 && !bFound; x--) { for(int y=0; y<=rect.bottom-1 && !bFound; y++) { COLORREF rgbColor=GetPixel(hDCMem,x,y); if(rgbColor!=RGB(255,255,255)) { //found a non-white pixel, save the horizontal position //and exit the loops. Job finished. iXmax=x; bFound=TRUE; } } } //this is the width of the text painted in italic font! LONG lWidthOfText=iXmax+1;//+1 because we use 0-based indexes
一些评论
- 绘制的文本可能比使用
GetTextExtentPoint32
计算的文本窄。例如,请参见 Verdana 12 斜体。 - 在对话框中,编辑控件具有要考虑的左/右和上/下边距。我没有在此示例中使用编辑控件。
- 在对话框中,标签控件似乎宽 +1 像素,除非使用
SS_SIMPLE
样式。参见下图。
历史
- 版本 1.0 [2006 年 7 月 23 日] - 创建
- 版本 1.1 [2006 年 8 月 6 日] - 新的复选框,用于查看将“最后一个字符的上伸”添加到计算文本宽度的经典方法中将如何有助于获得更准确的结果。
像这样
SIZE sizeText; GetTextExtentPoint32(hDCMem, szText, lstrlen(szText), &sizeText); LONG lWidthOfText= sizeText.cx; ABCFLOAT WidthsABC[256]; GetCharABCWidthsFloat(hDCMem, 0, 255, WidthsABC); // overhang of the last character double dOverhangTrailing = WidthsABC[szText[lstrlen(szText)-1]].abcfC; if(dOverhangTrailing<0) { //if the overhang is negative then adjust //the calculated width of the text lWidthOfText-=dOverhangTrailing; }
但正如上面已经提到的,本文建议另一种方法,不需要使用任何这些下伸和上伸值。
- 版本 1.2 [2006 年 8 月 17 日] - 调整了从右到左的像素颜色扫描算法。
将要扫描的边界矩形的右边界设置为 '
sizeText.cx+widthOfTheLastCharacter
',正如 'oupoi' 建议的那样。我们现在还有一个新的复选框来测试使用 Mihai Nita 的技巧是否有助于更快地计算,当然,同时也能获得准确的结果。
除非我在某个地方犯了错误,否则我对使用 Mihai Nita 的技巧所获得的结果的精度不满意。
像这样
//fill with white FillRect(hDCMem,&rect,(HBRUSH)GetStockObject(WHITE_BRUSH)); // Added this by Mihai Nita - August 17, 2006 // trick, set text background colour to black, before painting! SetBkColor(hDCMem,RGB(0,0,0)); //reality shows that DrawText fails to properly //paint the surrounding rectangle (??) DrawText(hDCMem,szText,-1, &rect, DT_LEFT|DT_TOP|DT_SINGLELINE|DT_NOPREFIX); int iXmax=0; BOOL bFound=FALSE; int iYmed=(rect.bottom+rect.top)/2; // middle for(int x=rect.right-1;x>=0 && !bFound ;x--) { COLORREF rgbColor=GetPixel(hDCMem,x,iYmed); if(rgbColor!=RGB(255,255,255)) { iXmax=x; bFound=TRUE; } }
- 版本 1.3 [2018 年 7 月 25 日] - 重新编译了演示程序以显示区域字符,如 șȚ
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。