Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

How to get a character size in pixels?

Posted on 2008-10-23
12
Medium Priority
?
1,006 Views
Last Modified: 2011-10-19
Hi,
I would like to draw on a given dc some text bounded by a rect.
The problem is that I don't want to get the font height but the character height ( or the max height of a string ). Some font use characters that are much smaller then there font size. an "M" does not reach the top of the font and a "g" does not reach the bottom of the font.

Please advice,
Udi Raz
0
Comment
Question by:UdiRaz
12 Comments
 
LVL 86

Accepted Solution

by:
jkr earned 2000 total points
ID: 22788382
You can get that using 'GetTextExtentPoint32()' (http://msdn.microsoft.com/en-us/library/aa911424.aspx), see also http://msdn.microsoft.com/en-us/library/ms534015(VS.85).aspx ("String Widths and Heights")
0
 
LVL 13

Expert Comment

by:numberkruncher
ID: 22788398
Here are some snippets that I used a while back when I was creating a custom text editor.

// If you are using MFC:
CDC dc;
CSize size = dc.GetTextExtent("My String");

// If you are using Win32 SDK
SIZE size = {0};
::GetTextExtentPoint(hdc, "My String", strlen("My String"), &size);

0
 
LVL 31

Expert Comment

by:Zoppo
ID: 22788615
Hi UdiRa,

further you can use 'DrawText' with flag 'DT_CALCRECT' - this is helpful if you want the size of multiline texts if you have a given width to draw to.

ZOPPO
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 22788858
To add to above comments:

For all these functions you must have selected the font to the dc by calling SelectObject. And it actually works only if the dc was associated to an existing window (hwnd) and was ready for painting.
0
 
LVL 13

Expert Comment

by:numberkruncher
ID: 22816866
Hi UdiRaz

Did you manage to solve your problem? or did you need any further assistance?
0
 

Author Comment

by:UdiRaz
ID: 22819408
I'm sorry guys, my boss ask me to do something else so it will take me couple of day to get back into it.

Thanks
0
 

Author Comment

by:UdiRaz
ID: 22831513
Both GetTextExtent and GetTextExtentPoint32 returns the font size as height and not the real height of the text.

I created a dialog and draw some characters in english and Hindi using a font that supports both languages.

below is a code snippet and an image of the result.
void CFontSizeTestDlg::OnBnClickedDrawText()
{
	CFont tmpFont;
 
	VERIFY(tmpFont.CreateFont(
		22,						    // nHeight
		0,                         // nWidth
		0,                         // nEscapement
		0,                         // nOrientation
		FW_REGULAR,				   // nWeight
		FALSE,                     // bItalic
		FALSE,                     // bUnderline
		0,                         // cStrikeOut
		ANSI_CHARSET,              // nCharSet
		OUT_DEFAULT_PRECIS,        // nOutPrecision
		CLIP_DEFAULT_PRECIS,       // nClipPrecision
		DEFAULT_QUALITY,           // nQuality
		DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily
		_T("Mangal")));  // lpszFacename
 
	// Do something with the font just created...
	CDC* dc = GetDC();
	dc->SelectObject(&tmpFont);
 
//	TEXTMETRIC txt;
//	dc->GetTextMetrics( &txt);
	CRect rect;
	rect.SetRect( CPoint(10, 10), CPoint(300, 100));
 
	LPTSTR hindiString = new TCHAR[2];
	hindiString[0] = 0x915;
	hindiString[1] = 0;
	CString str = _T("Mg - ");
	str += hindiString;
 
	dc->DrawText(str, rect, DT_CENTER | DT_VCENTER);
 
	//////////////////////////////////////////////////////
 
	CSize size = dc->GetTextExtent(_T("My String"));
	CSize size1 = dc->GetTextExtent(hindiString);
 
	LPSIZE lpSize = new SIZE, lpSize1 = new SIZE;
	::GetTextExtentPoint32(dc->GetSafeHdc(), _T("My String"), 9, lpSize );
	::GetTextExtentPoint32(dc->GetSafeHdc(), hindiString, 1, lpSize1 );
 
}

Open in new window

drawText.JPG
0
 
LVL 13

Expert Comment

by:numberkruncher
ID: 22831693
dc->GetTextExtent should be returning the height of the white block behind the text.


Also when using the Win32 equivalents you could use the following instead of dynamically allocating memory and forgetting to delete it.
HDC safeHdc = dc->GetSafeHdc();
 
SIZE sizeA = {0}, sizeB = {0};
::GetTextExtentPoint32(safeHdc, _T("My String"), 9, &sizeA);
::GetTextExtentPoint32(safeHdc, hindiString, 1, &sizeB);

Open in new window

0
 
LVL 13

Expert Comment

by:numberkruncher
ID: 22831811
If you are trying to find the height of actual text (i.e. black ink) then you will probably need to manually measure the ink by finding the topmost position of black, and the bottommost position of black, and then calculating the distance between. I don't know of a Win32 API function which will do this for you.

This could of course become a slow process if you are working with a lot of text.
0
 

Author Comment

by:UdiRaz
ID: 22838426
1. the height of the whiteblock is the font size
2. I do expect to find an api since there are a lot of params that define a character glyph as describes here: http://msdn.microsoft.com/en-us/library/ms534015(VS.85).aspx

I am looking for an api that input a character and return these values.
0
 

Author Comment

by:UdiRaz
ID: 22838454
I found this method : GetGlyphOutline. But I think I am using it in a wrong way becuase I don't get the expected result.
/****************************************************************************
 *
 *  FUNCTION   : PaintIt(hWnd)
 *
 *  PURPOSE    : Demonstrate getting the glyph information by filling the 
 *               window with a whole bunch of characters.  Very simplistic
 *               character placement is used.
 *
 *  RETURNS    : None.
 *
 ****************************************************************************/
void PASCAL NEAR PaintIt(HWND hWnd)
{
    PAINTSTRUCT ps;
    HDC hdcScreen;
    LOGFONT lf;
    TEXTMETRIC tm;
    WORD x = 0, y = 0;
    UINT i;
    HFONT hfOld;
	GLYPHMETRICS gm;
	MAT2 mat;
 
    hdcScreen = BeginPaint(hWnd, &ps);
    hfOld = SelectObject(hdcScreen, hMyFont);
    GetTextMetrics(hdcScreen, &tm);
	IdentityMat(&mat);
 
	GetGlyphOutline( hdcScreen, 'm', GGO_NATIVE, &gm, 0, NULL, &mat);
 
	OutputGlyph(hdcScreen, 'm', x, y, tm.tmAscent);
	x += (tm.tmMaxCharWidth+2)/2;
	if (x > 600)     // Extremely arbitrary hardcoded width.
	{
		x = 0;
		y += tm.tmHeight + 5;
	}
	GetGlyphOutline( hdcScreen, 'i', GGO_NATIVE, &gm, 0, NULL, &mat);
 
	OutputGlyph(hdcScreen, 'i', x, y, tm.tmAscent);
	x += (tm.tmMaxCharWidth+2)/2;
	if (x > 600)     // Extremely arbitrary hardcoded width.
	{
		x = 0;
		y += tm.tmHeight + 5;
	}
 
	GetGlyphOutline( hdcScreen, 'M', GGO_NATIVE, &gm, 0, NULL, &mat);
 
	OutputGlyph(hdcScreen, 'M', x, y, tm.tmAscent);
	x += (tm.tmMaxCharWidth+2)/2;
	if (x > 600)     // Extremely arbitrary hardcoded width.
	{
		x = 0;
		y += tm.tmHeight + 5;
	}
 
	GetGlyphOutline( hdcScreen, 'g', GGO_NATIVE, &gm, 0, NULL, &mat);
 
	OutputGlyph(hdcScreen, 'g', x, y, tm.tmAscent);
	x += (tm.tmMaxCharWidth+2)/2;
	if (x > 600)     // Extremely arbitrary hardcoded width.
	{
		x = 0;
		y += tm.tmHeight + 5;
	}
 
	hfOld = SelectObject(hdcScreen, hMyHindiFont);
	GetTextMetrics(hdcScreen, &tm);
	IdentityMat(&mat);
 
	GetGlyphOutline( hdcScreen, 0x915, GGO_NATIVE, &gm, 0, NULL, &mat);
 
	OutputGlyph(hdcScreen, 0x915, x, y, tm.tmAscent);
	x += (tm.tmMaxCharWidth+2)/2;
	if (x > 600)     // Extremely arbitrary hardcoded width.
	{
		x = 0;
		y += tm.tmHeight + 5;
	}
 
    SelectObject(hdcScreen, hfOld);
    EndPaint(hWnd, &ps);
}

Open in new window

0
 
LVL 13

Expert Comment

by:numberkruncher
ID: 22843902
I believe that 'GetGlyphOutline' extracts the specified character from a font and puts it into a buffer. Most vector fonts should contain the data you need (gmBlackBoxY) but this approach would require you to decide between:

Fast performance - Preload the desired height of all glyph characters into an integer array, and then refer to these at a later stage. Could be slow during caching process, but would also require more memory.

Efficient memory and initial startup performance - Get glyph character by character, BUT this will probably be extremely slow, especially if you have a lot of text to process character by character.

I used this function a very long time ago to pre-render a font into a bitmap so that I could display it in a video game. Here is a link that demonstrates how this function can be used (the example is some way down the page):
http://www.gamedev.net/community/forums/topic.asp?topic_id=443128
0

Featured Post

Vote for the Most Valuable Expert

It’s time to recognize experts that go above and beyond with helpful solutions and engagement on site. Choose from the top experts in the Hall of Fame or on the right rail of your favorite topic page. Look for the blue “Nominate” button on their profile to vote.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
Often, when implementing a feature, you won't know how certain events should be handled at the point where they occur and you'd rather defer to the user of your function or class. For example, a XML parser will extract a tag from the source code, wh…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

773 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question