Solved

How to get a character size in pixels?

Posted on 2008-10-23
12
979 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
12 Comments
 
LVL 86

Accepted Solution

by:
jkr earned 500 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
Independent Software Vendors: 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

Technology Partners: 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!

Question has a verified solution.

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

This article will show you some of the more useful Standard Template Library (STL) algorithms through the use of working examples.  You will learn about how these algorithms fit into the STL architecture, how they work with STL containers, and why t…
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

751 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