Jump to content
  • Advertisement
Sign in to follow this  
CGameProgrammer

Cannot reliably measure width of fonts

This topic is 3213 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm rendering a font to a texture so I can compose strings using polygonal meshes. In order to support italic fonts (where the characters need to be closer together than the actual widths of the glyphs) and fonts with characters that overhang the left or right edges, I have determined after much research that the Win32 API function GetCharABCWidths is the best one to use. It returns a structure containing three values: the offset added to the caret before rendering begins, the actual width of the character, and the offset added after rendering to indicate where the next glyph should be drawn. Testing with 18-point italic Impact, the letter "O" returns {2, 14, -3}. That means drawing starts two pixels to the right of the caret, the italic O should be exactly 14 pixels wide (with no antialiasing) and then the caret should be moved three pixels to the left before drawing the next glyph, or 2+14-3 = 13 pixels to the right of the original location. That works very well. The problem is it returns incorrect values for certain fonts, even without italics. For example 18-point regular Microsoft Sans Serif returns {1, 15, 1} but the actual rendered width of the O character is 17 pixels, not 15. Why are different fonts inconsistent? What is a 100% reliable (for TrueType fonts) method of determining character width and spacing?

Share this post


Link to post
Share on other sites
Advertisement
You have to set a current font. Asking Windows to build your fonts doesn't mean that will be the current font, and all string measurements use the current font.

YourCurrentFont = CreateFont( ..... );
SelectObject(win_state.hDC, YourCurrentFont);

Sorry, I've just read through. Anyway, maybe you didn't set/update the current truetype font.

Share this post


Link to post
Share on other sites
Quote:
Original post by CGameProgrammer

That works very well. The problem is it returns incorrect values for certain fonts, even without italics. For example 18-point regular Microsoft Sans Serif returns {1, 15, 1} but the actual rendered width of the O character is 17 pixels, not 15.


That 'two pixels too wide' issue sounds like subpixel-rendering.

Share this post


Link to post
Share on other sites
There was no subpixel rendering since I was using non-antialiased fonts. But in fact I solved the problem - the font height passed in to CreateFont was incorrect. I'm using C# and was using the 'Height' member of the C# Font object, which on a 96-DPI display is 28 pixels for an 18-pt font. But it seems this is wrong. The actual correct value to pass in is -24 in this case. The code, found on MSDN somewhere, is:

int pixels = -MulDiv( points, GetDeviceCaps(hdc, LOGPIXELSY), 72);

...where points=18 in this case. 72 is how many points there are per inch and this is universal, not a display setting. It's not the same as dots per inch.

Anyway, this works. The completed test code is:


#include <Windows.h>
#include <stdio.h>

ABC Measure ( int size, const char* type, bool italic, char ascii )
{
HWND window = GetDesktopWindow();
HDC dc = GetDC( window );

int fontHeight = -MulDiv( size, GetDeviceCaps(dc, LOGPIXELSY), 72 );
HFONT font = CreateFont( fontHeight,
0,
0,
0,
400, // weight = normal
italic ? 1 : 0,
0,
0,
ANSI_CHARSET,
OUT_TT_ONLY_PRECIS,
CLIP_DEFAULT_PRECIS,
NONANTIALIASED_QUALITY,
0,
type );

HGDIOBJ old = SelectObject( dc, font );

ABC abc;
GetCharABCWidths( dc, (UINT)ascii, (UINT)ascii, &abc );

SelectObject( dc, old );
ReleaseDC( window, dc );

return abc;
}

int main ( )
{
ABC abcO = Measure( 18, "Microsoft Sans Serif", false, 'O' );

printf( "O: %i, %i, %i\n", abcO.abcA, abcO.abcB, abcO.abcC );

return 0;
}


Output is {1,17,1} on a 96-DPI display.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!