65.9K
CodeProject 正在变化。 阅读更多。
Home

斜体字体的文本宽度

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (24投票s)

2006年7月24日

CPOL

3分钟阅读

viewsIcon

150461

downloadIcon

1638

展示了 GetCharABCWidths 和 GetCharABCWidthsFloat 的替代方法

引言

我们的目的是准确测量斜体文本的宽度。

Using the Code

GetTextExtentPoint32 Win32 API 函数或带有 DT_CALCRECT 标志的 DrawText Win32 API 函数,并*不能*告诉我们斜体文本的正确宽度,只有计算出的高度是正确的。对于大多数斜体字体,使用这两个函数计算出的宽度太窄,显示的文本看起来被右侧截断了。

我们可以尝试使用 GetCharABCWidthsGetCharABCWidthsFloat Win32 API 函数。这些函数将为我们提供有关我们感兴趣的文本中每个字符的下伸和上伸的信息。

请查看这些 MSDN 文章,了解有关使用下伸和上伸值的更多详细信息

此 EXE 的更新版本包含一个新的复选框,用于查看添加“最后一个字符的上伸”如何有助于获得更准确的结果。

但是,在本文中,我建议另一种方法

  1. 在填充了白色的内存设备上下文中,以黑色绘制文本,如下所示
    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);
  2. 然后,从右到左扫描内存设备上下文中像素的颜色,如下所示
    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 日] - 重新编译了演示程序以显示区域字符,如 șȚ

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.