Article ID: 200262
Article Last Modified on 2/12/2007
BOOL GetTextBlackExtentPoint(
HDC hdc, // handle to DC
LPCTSTR lpString, // text string
int cbString, // number of characters in string
LPSIZE lpSize // string size
)
{
SIZE sizeTx;
ABC lead;
ABC trail;
TCHAR ch = lpString[lstrlen(lpString)-1];
if (NULL == lpSize)
return false;
if (!GetTextExtentPoint32(hdc, lpString, cbString, &sizeTx))
return false;
if (!GetCharABCWidths(hdc, lpString[0], lpString[0], &lead))
return false;
if (!GetCharABCWidths(hdc, ch, ch, &trail))
return false;
sizeTx.cx = sizeTx.cx - lead.abcA -trail.abcC;
*lpSize = sizeTx;
return true;
}
BOOL SnugTextOut( HDC hDC, RECT &rc, LPTSTR szString )
{
int i, nStringLength;
BOOL bResult;
int *pDx;
int nX = rc.left;
int nY = rc.top;
int Width = 0;
// How long is the string - you need this later in this code.
nStringLength = lstrlen( szString );
// Allocate enough memory for the intercharacter spacing array.
pDx = (int *)new int[ sizeof(int) * nStringLength ];
// Initialize the array with the standard values.
for(i=0; i < nStringLength; i++)
{
ABC abc;
if( ! GetCharABCWidths( hDC, szString[i], szString[i], &abc ) )
{
delete [] pDx;
return FALSE;
}
pDx[i] = abc.abcA + abc.abcB + abc.abcC;
// You need the width.
Width += pDx[i];
// Also, account for the Black extent of the string.
if (i == 0)
{
// Adjustment before the first character for underhang
nX -= abc.abcA;
Width -= abc.abcA;
}
if (i == nStringLength-1)
{
// Adjustment for overhang
Width -= abc.abcC;
}
}
int deltaCX = rc.right-rc.left - Width;
int deltaCh = deltaCX / nStringLength;
int remainder = deltaCX % nStringLength;
int error = 0;
// Distribute the adjustment through the intercharacter spacing.
// For a more typographically correct approach, distribute the
// adjustment in the "white space."
for(i=0; i < nStringLength; i++)
{
pDx[i] += deltaCh;
error += remainder;
if (abs(error) >= nStringLength) // adjustment?
{
int adjustment = abs(error)/error;
pDx[i] += adjustment;
error -= nStringLength*adjustment;
}
}
// ExtTextOut() draws our text with our ICS array.
bResult = ExtTextOut( hDC, nX, nY, 0, &rc, szString, nStringLength, pDx );
// Clean up.
delete [] pDx;
return bResult;
}
double ScaleFactor = 1.0;
BOOL OnPaint(HWND hWnd)
{
PAINTSTRUCT ps;
HDC hdc;
LOGFONT lf;
HFONT hFont, hScaledFont, hOldFont;
SIZE sizeText;
TCHAR Buffer[] = TEXT("for this is sample text of");
RECT rcText;
hdc = BeginPaint(hWnd, &ps);
// Get the unscaled metrics.
ZeroMemory(&lf, sizeof(lf));
lf.lfHeight = -MulDiv( 14, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
lf.lfCharSet = ANSI_CHARSET;
lf.lfItalic = true;
lstrcpy(lf.lfFaceName, TEXT("Times New Roman"));
hFont = CreateFontIndirect(&lf);
hOldFont = (HFONT)SelectObject(hdc, hFont);
GetTextBlackExtentPoint(hdc, Buffer, lstrlen(Buffer), &sizeText);
SelectObject(hdc, hOldFont);
// Draw the scaled text.
lf.lfHeight = -MulDiv( 14*ScaleFactor*100, GetDeviceCaps(hdc, LOGPIXELSY), 7200 );
lf.lfCharSet = ANSI_CHARSET;
lf.lfItalic = true;
lstrcpy(lf.lfFaceName, TEXT("Times New Roman"));
hScaledFont = CreateFontIndirect(&lf);
SelectObject(hdc, hScaledFont);
rcText.left = 100;
rcText.top = 100;
rcText.right = rcText.left + sizeText.cx*ScaleFactor;
rcText.bottom = rcText.top + sizeText.cy*ScaleFactor;
SnugTextOut(hdc, rcText, Buffer);
SelectObject(hdc, GetStockObject(NULL_BRUSH));
// Rectangles are right-bottom exclusive.
rcText.right++;
rcText.bottom++;
Rectangle(hdc, rcText.left, rcText.top, rcText.right, rcText.bottom);
// Clean up.
SelectObject(hdc, hOldFont);
DeleteObject(hFont);
DeleteObject(hScaledFont);
EndPaint(hWnd, &ps);
return true;
}
To see this sample code in action, create a simple Win32 Windows application, and use the OnPaint function to process the WM_PAINT message. For some user interaction such as a keystroke, a menu item selection, or a mouse click, change the ScaleFactor variable, and then invalidate the client area.
Additional query words: 3.51 4.00 KbDSI kbDSupport GdiFnt GdiTt
Keywords: kbdswgdi2003swept kbfont kbgdi kbhowto KB200262