Click here to Skip to main content
15,867,330 members
Articles / Multimedia / GDI

Width of Text in Italic Font

Rate me:
Please Sign up or sign in to vote.
4.85/5 (26 votes)
25 Jul 2018CPOL3 min read 142.8K   1.6K   37   29
Shows an alternative to GetCharABCWidths and GetCharABCWidthsFloat

Image 1

Introduction

Our purpose is to measure accurately the width of text written in italic font.

Using the Code

The GetTextExtentPoint32 Win32 API function or the DrawText Win32 API function with the DT_CALCRECT flag, will -not- tell us the correct width of text in italic font, only the calculated height is correct. For most of the italic fonts, using these two functions, the calculated width is too narrow and the displayed text looks right-trimmed.

We could try using the GetCharABCWidths or GetCharABCWidthsFloat Win32 API functions. These functions will give us information about the underhang and the overhang of each character of the text we're interested in.

Please check these MSDN articles for more details about using the underhang and overhang values:

This updated version of the EXE includes a new checkbox, to see how adding the 'overhang of the last character' will help in getting a more accurate result.

However, in this article, I am suggesting another approach:

  1. Paint the text in black colour, in a memory device context filled with white colour, like this:
    C++
    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. Then, scan the colour of the pixels in the memory device context, from right-to-left, like this:
    C++
    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

Image 2

A few comments:

  • Painted text can be narrower than text calculated with GetTextExtentPoint32. See, for example, Verdana 12 Italic.
  • In a dialog box, the edit controls have left/right and up/down margins to take into account. I have not used edit controls in this sample.
  • In a dialog box, the label controls seem to be +1 pixel wider, unless the SS_SIMPLE style is used. See the image below.

Image 3

History

  • Version 1.0 [July 23, 2006] - Created
  • Version 1.1 [August 6, 2006] - New checkbox to see how adding the 'overhang of the last character' to the classic method of calculating the text width will help in getting a more accurate result.

    Like this:

    C++
    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;
    }

    But as already mentioned above, this article is suggesting another method, which doesn't require using any of these underhang and overhang values.

  • Version 1.2 [August 17, 2006] - Adjustment to the right-to-left pixel colour scanning algorithm.

    Set the right-limit of the bounding rectangle to be scanned to just 'sizeText.cx+widthOfTheLastCharacter', as suggested by 'oupoi'.

    We now also have a new checkbox to test if using the Mihai Nita's trick will help in getting a faster calculation and at the same time, of course, an accurate result.

    Unless I've made a mistake somewhere, I'm not satisfied by the precision of the result by using the Mihai Nita's trick.

    Like this:

    C++
    //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;
      }
    }
  • Version 1.3 [July 25, 2018] - Recompiled the demo program to display regional characters like șȚ

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Romania Romania
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionFull sources please Pin
The_Immortal16-Jun-23 17:05
The_Immortal16-Jun-23 17:05 
QuestionCan I use it with C#? Pin
Umut Comlekcioglu24-Feb-15 7:38
professionalUmut Comlekcioglu24-Feb-15 7:38 
SuggestionStill not a hundred percent accurate Pin
Dai Fei10-Mar-14 6:05
Dai Fei10-Mar-14 6:05 
GeneralAbout text width [modified] Pin
Zheyi K19-May-08 2:06
Zheyi K19-May-08 2:06 
GeneralRe: About text width Pin
Alexandru Matei19-May-08 2:18
Alexandru Matei19-May-08 2:18 
GeneralInteresting Pin
kjsfuture16-May-08 18:20
kjsfuture16-May-08 18:20 
GeneralHelp me Pin
kjsfuture16-May-08 17:19
kjsfuture16-May-08 17:19 
GeneralRe: Help me [modified] Pin
Alexandru Matei16-May-08 20:22
Alexandru Matei16-May-08 20:22 
GeneralInteresting Pin
Waldermort24-Aug-06 22:33
Waldermort24-Aug-06 22:33 
GeneralNice, but... Pin
oupoi16-Aug-06 18:18
oupoi16-Aug-06 18:18 
GeneralRe: Nice, but... Pin
Alexandru Matei16-Aug-06 20:40
Alexandru Matei16-Aug-06 20:40 
GeneralIt seems quite inefficient Pin
Mihai Nita16-Aug-06 9:41
Mihai Nita16-Aug-06 9:41 
GeneralRe: It seems quite inefficient [modified] Pin
Alexandru Matei16-Aug-06 10:14
Alexandru Matei16-Aug-06 10:14 
GeneralRe: It seems quite inefficient [modified] Pin
Alexandru Matei16-Aug-06 11:37
Alexandru Matei16-Aug-06 11:37 
GeneralRe: It seems quite inefficient Pin
Mihai Nita16-Aug-06 22:56
Mihai Nita16-Aug-06 22:56 
GeneralRe: It seems quite inefficient [modified] Pin
Alexandru Matei16-Aug-06 23:33
Alexandru Matei16-Aug-06 23:33 
GeneralRe: It seems quite inefficient Pin
Mihai Nita20-Aug-06 10:40
Mihai Nita20-Aug-06 10:40 
GeneralRe: It seems quite inefficient [modified] Pin
Alexandru Matei20-Aug-06 18:58
Alexandru Matei20-Aug-06 18:58 
GeneralDT_CALCRECT and GetCharABCWidths() [modified] Pin
bob1697230-Jul-06 18:16
bob1697230-Jul-06 18:16 
GeneralRe: DT_CALCRECT and GetCharABCWidths() [modified] Pin
Alexandru Matei30-Jul-06 21:38
Alexandru Matei30-Jul-06 21:38 
GeneralRe: DT_CALCRECT and GetCharABCWidths() [modified] Pin
bob1697231-Jul-06 3:14
bob1697231-Jul-06 3:14 
GeneralRe: DT_CALCRECT and GetCharABCWidths() [modified] Pin
Alexandru Matei31-Jul-06 8:01
Alexandru Matei31-Jul-06 8:01 
GeneralRe: DT_CALCRECT and GetCharABCWidths() Pin
bob1697231-Jul-06 9:19
bob1697231-Jul-06 9:19 
I'm not sure. I get the same results for both. (This is using your code snippets inserted into the OnDraw function at appropriate locations commenting out whatever code needed to be commented out)

J 0 13 -4
6 2 12 -3

J 0 13 -4
6 2 12 -3

GeneralRe: DT_CALCRECT and GetCharABCWidths() Pin
Alexandru Matei31-Jul-06 9:20
Alexandru Matei31-Jul-06 9:20 
GeneralUsing DT_CALCRECT Pin
Pharago25-Jul-06 14:10
Pharago25-Jul-06 14:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.