Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Win32 API and VFP: Calculating font height for CreateFont()

Status
Not open for further replies.

awaresoft

Programmer
Feb 16, 2002
373
DE
I'm trying to calculate font height for the the Win32 API CreateFont() function. I'm creating a font structure (hFont) that is later used in the DrawText() function for determining the vertical height of a word wrapped string displayed in a specific font.

My problem is that I'm not getting accurate results returned from DrawText() and I believe (???) this is due to bad fontheight calculation on my part.

Here's what I have so far - watch line wrap:

Assuming pcText (to be used for DrawText output), pcFontName, pnFontSize (in points), and lnBold (font weight), and boolean llItalic, llUnderline, and llStrikeThru variables contain font specific info ...

Code:
* DrawText() constants
#define DT_LEFT      	 	  	  0
#define DT_CENTER    	 	  	  1
#define DT_TOP       	 	  	  0
#define DT_RIGHT     	 	  	  2
#define DT_WORDBREAK		 	 16  && wordwrap
#define DT_EXTERNALLEADING    0x200  && includes font’s external leading in the line height

#define DT_CALCRECT		  	  0x400  && calculate height without displaying text
#define DT_EDITCONTROL	 	 0x2000  && use editbox height and wrapping calculations
#define DT_MODIFYSTRING 	0x10000  && update text with ellipses
#define DT_NOPREFIX		  	  0x800  && disable & expansion to _

#define DT_END_ELLIPSIS  	 0x8000  && ellipsis at end of text
#define DT_PATH_ELLIPSIS 	 0x4000  && ellipsis in middle of string (for paths)
#define DT_WORD_ELLIPSIS	0x40000  && ellipsis at end of text on word boundry

* CreateFont() constants
#define ANSI_CHARSET          0
#define OUT_DEFAULT_PRECIS    0
#define OUT_DEVICE_PRECIS     5
#define OUT_OUTLINE_PRECIS    8
#define CLIP_DEFAULT_PRECIS   0
#define CLIP_STROKE_PRECIS    2
#define DEFAULT_QUALITY       0
#define PROOF_QUALITY         2
#define DEFAULT_PITCH         0
#define FW_BOLD             700		&& use to bold

* GetDeviceCaps() constants
#define LOGPIXELSY			 90

* determine font height
* NOTE: - MulDiv( pnFontSize, GetDeviceCaps( hDC, LOGPIXELSY ), 72 )
local lnFontHeight
lnFontHeight = - int( pnFontSize * GetDeviceCaps( hDC, LOGPIXELSY ) / 72 )

* create a new font to use for our text calculations
local hFont
hFont = CreateFont( ;
lnFontHeight, 0, 0, 0, lnBold, llItalic, llUnderLine, llStrikeThru, ;
ANSI_CHARSET,OUT_OUTLINE_PRECIS, CLIP_STROKE_PRECIS, PROOF_QUALITY, DEFAULT_PITCH, pcFontName ) 

* select new font into the device context and delete the old one
= DeleteObject( SelectObject( hdc, hFont ) )

* build DrawText() style flags
lnDTflags = DT_LEFT + DT_WORDBREAK + DT_CALCRECT + DT_EDITCONTROL + ;
  DT_MODIFYSTRING + DT_NOPREFIX + DT_WORD_ELLIPSIS + DT_EXTERNALLEADING
  
* determine size of pcText with specified pnWidth
* NOTE: Bounding rectangle defined by left, top, right, bottom coordinates vs. height, width.
* NOTE: We set bottom to a large value - if we set it too short then out height will be clipped.
local lcRect, lnHeight
lcRect = n2dw( 0 ) + n2dw( 0 ) + n2dw( pnWidth ) + n2dw( 1200 )
lnHeight = DrawText( hDC, @pcText, len( pcText ), @lcRect, lnDTflags )

* release system resources
= DeleteObject( hFont )
= ReleaseDC( hWindow, hDC )

* return height
return lnHeight

My ultimate goals are to use DrawText() to:

a. Determine the max amount of vertical space required to display a word wrapped string in order to create an autosizing editbox

b. Use DrawText() with the DT_MODIFYSTRING parm to get back trimmed strings with elllipsis (...) when I need to display more text than will fit in a specific rectangle. I can get this to work with single line strings - but not with strings that wrap and/or have carriage returns.

Any ideas appreciated,

Malcolm
 
The better way to calculate font height (vs. my MulDiv() implementation above) is to use the trusty Fontmetric() function. (Thanks to Stuart Dunkeld on Ed Leafe's ProfoxTech list).

Replace this:

Code:
lnFontHeight = - int( pnFontSize * GetDeviceCaps( hDC, LOGPIXELSY ) / 72 )

With this:

Code:
lnFontHeight = fontmetric( 1, pcFontName, pnFontSize, pcFontStyles )

This works much better than my original code, but I'm still not getting accurate DrawText() return values for all font, fontsize combinations. (I'm on XP SP2, using MS OO (OpenType) and MS True Type fonts).

For example, DrawText() in combination with the above CreateFont() code does NOT return accurate text height for the following (example) font/fontsize combinations (this list is not inclusive, its just the results of my random testing)

What's interesting (but not consistent) is that fontheights that are a multiple of 3 as returned by FontMetric() are the ones that don't work right. This doesn't happen with all font heights that are multiples of 3 - but my only failure scenarios are font heights with this characteristic.

Font name, points, width of editbox and result:

- Times New Roman, 10: 760 (short by 1 line), 400 (short by 2 lines) (fontheight = 15)

- Verdana, 12: 760 (short by 2 lines), 400 (short by 2 lines) (fontheight = 18)

Any ideas?

Malcolm
 
Is it perhaps a rounding issue, where the height (or the resulting spacing between lines) should be, say, 25.3 pixels. When adding up the height, rounded, 25 pixels will, every three lines, lose a line, where if the height were kept as a fraction, then every now and then it would be rendered as 26 pixels instead of 25.
Enough single-pixel-errors might add up to your missing lines.

- Bill

Get the best answers to your questions -- See FAQ481-4875.
 
Bill,

Thanks for the feedback. I share your conclusion but am puzzled why fractional values would be involved given that the Windows API calls involved with CreateFont() and DrawText() all appear to use pure integer math and in fact are properly calculating text height for editboxes - just not for me!

Another way for me to do this is to perform the word wrapping calculations myself. Another developer just shared some code that does this all in VFP. I was initially afraid to take this tact because of concerns over performance, but apparently the VFP only solution is pretty fast as well.

Malcolm
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top