You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
682 lines
20 KiB
682 lines
20 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: drawtext.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* This module contains common text drawing functions.
|
|
*
|
|
* History:
|
|
* 02-12-92 mikeke Moved Drawtext to the client side
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#define CR 13
|
|
#define LF 10
|
|
|
|
#define DT_HFMTMASK 0x03
|
|
|
|
/***************************************************************************\
|
|
* IsMetaFile
|
|
*
|
|
* History:
|
|
* 30-Nov-1992 mikeke Created
|
|
\***************************************************************************/
|
|
|
|
BOOL IsMetaFile(
|
|
HDC hdc)
|
|
{
|
|
DWORD dwType = GetObjectType(hdc);
|
|
return (dwType == OBJ_METAFILE ||
|
|
dwType == OBJ_METADC ||
|
|
dwType == OBJ_ENHMETAFILE ||
|
|
dwType == OBJ_ENHMETADC);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrawTextA (API)
|
|
*
|
|
* History:
|
|
* 30-11-92 mikeke Created
|
|
\***************************************************************************/
|
|
|
|
|
|
CONST WCHAR gwszNullStr[] = L"";
|
|
|
|
|
|
FUNCLOG6(LOG_GENERAL, int, DUMMYCALLINGTYPE, DrawTextExA, HDC, hdc, LPSTR, lpchText, int, cchText, LPRECT, lprc, UINT, format, LPDRAWTEXTPARAMS, lpdtp)
|
|
int DrawTextExA(
|
|
HDC hdc,
|
|
LPSTR lpchText,
|
|
int cchText,
|
|
LPRECT lprc,
|
|
UINT format,
|
|
LPDRAWTEXTPARAMS lpdtp)
|
|
{
|
|
LPWSTR lpwstr;
|
|
int iRet;
|
|
int iUniString;
|
|
WORD wCodePage = (WORD)GdiGetCodePage(hdc);
|
|
|
|
if (cchText == -1) {
|
|
// USER_AWCONV_COUNTSTRINGSZ does not count/convert trailing \0.
|
|
cchText = USER_AWCONV_COUNTSTRINGSZ;
|
|
} else if (cchText < -1) {
|
|
return 0;
|
|
}
|
|
|
|
if ((iUniString = MBToWCSEx(wCodePage, lpchText, cchText, &lpwstr, -1, TRUE)) == 0) {
|
|
if (cchText == USER_AWCONV_COUNTSTRINGSZ) {
|
|
lpwstr = (LPWSTR)gwszNullStr;
|
|
format &= ~DT_MODIFYSTRING;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Grow the buffer to accomodate the ellipsis (see AddEllipsisAndDrawLine)
|
|
*/
|
|
if (format & DT_MODIFYSTRING) {
|
|
int iNewLen = (iUniString + CCHELLIPSIS + 1) * sizeof(*lpwstr);
|
|
LPWSTR lpwstrNew = UserLocalReAlloc(lpwstr, iNewLen, HEAP_ZERO_MEMORY);
|
|
if (lpwstrNew == NULL) {
|
|
UserLocalFree((HANDLE)lpwstr);
|
|
return FALSE;
|
|
}
|
|
lpwstr = lpwstrNew;
|
|
}
|
|
|
|
iRet = DrawTextExWorker(hdc, lpwstr, iUniString, lprc, format, lpdtp, GetTextCharset(hdc));
|
|
|
|
if (format & DT_MODIFYSTRING) {
|
|
/*
|
|
* Note that if the buffer grew and the caller provided the string size,
|
|
* then we won't return the additional characters... fixing this
|
|
* might break some apps so let's leave it alone until some one complains
|
|
*/
|
|
if (cchText < 0) {
|
|
UserAssert(cchText == USER_AWCONV_COUNTSTRINGSZ);
|
|
// Guess how many bytes we can put in the buffer...
|
|
// We can safely assume the maximum bytes available.
|
|
// At worst, even for DBCS, the buffer size required
|
|
// will be smaller than or equal to the orignal size,
|
|
// because some DBCS characters would be substituted
|
|
// to SBC ".", which is one byte each.
|
|
// On the other hand, the number of characters converted
|
|
// is limited by both iUniString and cchText.
|
|
//
|
|
if (IS_DBCS_ENABLED()) {
|
|
cchText = iUniString * DBCS_CHARSIZE;
|
|
} else {
|
|
cchText = iUniString * sizeof(CHAR);
|
|
}
|
|
}
|
|
WCSToMBEx(wCodePage, lpwstr, iUniString, &lpchText, cchText, FALSE);
|
|
}
|
|
|
|
if (lpwstr != gwszNullStr) {
|
|
UserLocalFree((HANDLE)lpwstr);
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrawTextW (API)
|
|
*
|
|
* History:
|
|
* 30-11-92 mikeke Created
|
|
\***************************************************************************/
|
|
|
|
|
|
FUNCLOG5(LOG_GENERAL, int, DUMMYCALLINGTYPE, DrawTextW, HDC, hdc, LPCWSTR, lpchText, int, cchText, LPRECT, lprc, UINT, format)
|
|
int DrawTextW(
|
|
HDC hdc,
|
|
LPCWSTR lpchText,
|
|
int cchText,
|
|
LPRECT lprc,
|
|
UINT format)
|
|
{
|
|
DRAWTEXTPARAMS DTparams;
|
|
LPDRAWTEXTPARAMS lpDTparams = NULL;
|
|
|
|
/* v-ronaar: fix bug #24985
|
|
* Disallow negative string lengths, except -1 (which has special meaning).
|
|
*/
|
|
if (cchText < -1)
|
|
return(0);
|
|
|
|
if (format & DT_TABSTOP)
|
|
{
|
|
DTparams.cbSize = sizeof(DRAWTEXTPARAMS);
|
|
DTparams.iLeftMargin = DTparams.iRightMargin = 0;
|
|
DTparams.iTabLength = (format & 0xff00) >> 8;
|
|
lpDTparams = &DTparams;
|
|
format &= 0xffff00ff;
|
|
}
|
|
|
|
return DrawTextExW(hdc, (LPWSTR)lpchText, cchText, lprc, format, lpDTparams);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrawTextA (API)
|
|
*
|
|
* History:
|
|
* 30-11-92 mikeke Created
|
|
\***************************************************************************/
|
|
|
|
|
|
FUNCLOG5(LOG_GENERAL, int, DUMMYCALLINGTYPE, DrawTextA, HDC, hdc, LPCSTR, lpchText, int, cchText, LPRECT, lprc, UINT, format)
|
|
int DrawTextA(
|
|
HDC hdc,
|
|
LPCSTR lpchText,
|
|
int cchText,
|
|
LPRECT lprc,
|
|
UINT format)
|
|
{
|
|
DRAWTEXTPARAMS DTparams;
|
|
LPDRAWTEXTPARAMS lpDTparams = NULL;
|
|
|
|
/* v-ronaar: fix bug #24985
|
|
* Disallow negative string lengths, except -1 (which has special meaning).
|
|
*/
|
|
if (cchText < -1)
|
|
return(0);
|
|
|
|
if (format & DT_TABSTOP) {
|
|
DTparams.cbSize = sizeof(DRAWTEXTPARAMS);
|
|
DTparams.iLeftMargin = DTparams.iRightMargin = 0;
|
|
DTparams.iTabLength = (format & 0xff00) >> 8;
|
|
lpDTparams = &DTparams;
|
|
format &= 0xffff00ff;
|
|
}
|
|
|
|
return DrawTextExA(hdc, (LPSTR)lpchText, cchText, lprc, format, lpDTparams);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ClientTabTheTextOutForWimps
|
|
*
|
|
* effects: Outputs the tabbed text if fDrawTheText is TRUE and returns the
|
|
* textextent of the tabbed text.
|
|
*
|
|
* nCount Count of bytes in string
|
|
* nTabPositions Count of tabstops in tabstop array
|
|
* lpintTabStopPositions Tab stop positions in pixels
|
|
* iTabOrigin Tab stops are with respect to this
|
|
*
|
|
* History:
|
|
* 19-Jan-1993 mikeke Client side
|
|
* 13-Sep-1996 GregoryW This routine now calls the LPK(s) to handle text out.
|
|
* If no LPKs are installed, this defaults to calling
|
|
* UserLpkTabbedTextOut (identical behavior to what we
|
|
* had before supporting LPKs).
|
|
\***************************************************************************/
|
|
|
|
LONG TabTextOut(
|
|
HDC hdc,
|
|
int x,
|
|
int y,
|
|
LPCWSTR lpstring,
|
|
int nCount,
|
|
int nTabPositions,
|
|
CONST INT *lpTabPositions,
|
|
int iTabOrigin,
|
|
BOOL fDrawTheText,
|
|
int iCharset)
|
|
{
|
|
int cxCharWidth;
|
|
int cyCharHeight = 0;
|
|
|
|
if (nCount == -1 && lpstring) {
|
|
nCount = wcslen(lpstring);
|
|
}
|
|
if (!lpstring || nCount < 0 || nTabPositions < 0)
|
|
return 0;
|
|
|
|
|
|
// Check if it is SysFont AND the mapping mode is MM_TEXT;
|
|
// Fix made in connection with Bug #8717 --02-01-90 --SANKAR--
|
|
if (IsSysFontAndDefaultMode(hdc))
|
|
{
|
|
cxCharWidth = gpsi->cxSysFontChar;
|
|
cyCharHeight = gpsi->cySysFontChar;
|
|
} else {
|
|
cxCharWidth = GdiGetCharDimensions(hdc, NULL, &cyCharHeight);
|
|
if (cxCharWidth == 0) {
|
|
RIPMSG0(RIP_WARNING, "TabTextOut: GdiGetCharDimensions failed");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return (*fpLpkTabbedTextOut)(hdc, x, y, lpstring, nCount, nTabPositions,
|
|
lpTabPositions, iTabOrigin, fDrawTheText,
|
|
cxCharWidth, cyCharHeight, iCharset);
|
|
}
|
|
|
|
LONG UserLpkTabbedTextOut(
|
|
HDC hdc,
|
|
int x,
|
|
int y,
|
|
LPCWSTR lpstring,
|
|
int nCount,
|
|
int nTabPositions,
|
|
CONST INT *lpTabPositions,
|
|
int iTabOrigin,
|
|
BOOL fDrawTheText,
|
|
int cxCharWidth,
|
|
int cyCharHeight,
|
|
int iCharset)
|
|
{
|
|
SIZE textextent, viewextent, windowextent;
|
|
int initialx = x;
|
|
int cch;
|
|
LPCWSTR lp;
|
|
int iOneTab = 0;
|
|
RECT rc;
|
|
UINT uOpaque = (GetBkMode(hdc) == OPAQUE) ? ETO_OPAQUE : 0;
|
|
BOOL fStrStart = TRUE;
|
|
int ySign = 1; //Assume y increases in down direction.
|
|
|
|
UNREFERENCED_PARAMETER(iCharset); //Needed by lpk, but not us
|
|
/*
|
|
* If no tabstop positions are specified, then use a default of 8 system
|
|
* font ave char widths or use the single fixed tab stop.
|
|
*/
|
|
if (!lpTabPositions) {
|
|
// no tab stops specified -- default to a tab stop every 8 characters
|
|
iOneTab = 8 * cxCharWidth;
|
|
} else if (nTabPositions == 1) {
|
|
// one tab stop specified -- treat value as the tab increment, one
|
|
// tab stop every increment
|
|
iOneTab = lpTabPositions[0];
|
|
|
|
if (!iOneTab)
|
|
iOneTab = 1;
|
|
}
|
|
|
|
// Calculate if the y increases or decreases in the down direction using
|
|
// the ViewPortExtent and WindowExtents.
|
|
// If this call fails, hdc must be invalid
|
|
if (!GetViewportExtEx(hdc, &viewextent))
|
|
return 0;
|
|
GetWindowExtEx(hdc, &windowextent);
|
|
if ((viewextent.cy ^ windowextent.cy) & 0x80000000)
|
|
ySign = -1;
|
|
|
|
rc.left = initialx;
|
|
rc.top = y;
|
|
rc.bottom = rc.top + (ySign * cyCharHeight);
|
|
|
|
while (TRUE) {
|
|
// count the number of characters until the next tab character
|
|
// this set of characters (substring) will be the working set for
|
|
// each iteration of this loop
|
|
for (cch = nCount, lp = lpstring; cch && (*lp != TEXT('\t')); lp++, cch--)
|
|
{
|
|
}
|
|
|
|
// Compute the number of characters to be drawn with textout.
|
|
cch = nCount - cch;
|
|
|
|
// Compute the number of characters remaining.
|
|
nCount -= cch + 1;
|
|
|
|
// get height and width of substring
|
|
if (cch == 0) {
|
|
textextent.cx = 0;
|
|
textextent.cy = cyCharHeight;
|
|
} else
|
|
GetTextExtentPointW(hdc, lpstring, cch, &textextent);
|
|
|
|
if (fStrStart)
|
|
// first iteration should just spit out the first substring
|
|
// no tabbing occurs until the first tab character is encountered
|
|
fStrStart = FALSE;
|
|
else
|
|
{
|
|
// not the first iteration -- tab accordingly
|
|
|
|
int xTab;
|
|
int i;
|
|
|
|
if (!iOneTab)
|
|
{
|
|
// look thru tab stop array for next tab stop after existing
|
|
// text to put this substring
|
|
for (i = 0; i < nTabPositions; i++)
|
|
{
|
|
xTab = lpTabPositions[i];
|
|
|
|
if (xTab < 0)
|
|
// calc length needed to use this right justified tab
|
|
xTab = (iTabOrigin - xTab) - textextent.cx;
|
|
else
|
|
// calc length needed to use this left justified tab
|
|
xTab = iTabOrigin + xTab;
|
|
|
|
if (x < xTab)
|
|
{
|
|
// we found a tab with enough room -- let's use it
|
|
x = xTab;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == nTabPositions)
|
|
// we've exhausted all of the given tab positions
|
|
// go back to default of a tab stop every 8 characters
|
|
iOneTab = 8 * cxCharWidth;
|
|
}
|
|
|
|
// we have to recheck iOneTab here (instead of just saying "else")
|
|
// because iOneTab will be set if we've run out of tab stops
|
|
if (iOneTab)
|
|
{
|
|
if (iOneTab < 0)
|
|
{
|
|
// calc next available right justified tab stop
|
|
xTab = x + textextent.cx - iTabOrigin;
|
|
xTab = ((xTab / iOneTab) * iOneTab) - iOneTab - textextent.cx + iTabOrigin;
|
|
}
|
|
else
|
|
{
|
|
// calc next available left justified tab stop
|
|
xTab = x - iTabOrigin;
|
|
xTab = ((xTab / iOneTab) * iOneTab) + iOneTab + iTabOrigin;
|
|
}
|
|
x = xTab;
|
|
}
|
|
}
|
|
|
|
if (fDrawTheText) {
|
|
|
|
/*
|
|
* Output all text up to the tab (or end of string) and get its
|
|
* extent.
|
|
*/
|
|
rc.right = x + textextent.cx;
|
|
ExtTextOutW(
|
|
hdc, x, y, uOpaque, &rc, (LPWSTR)lpstring,
|
|
cch, NULL);
|
|
rc.left = rc.right;
|
|
}
|
|
|
|
// Skip over the tab and the characters we just drew.
|
|
x += textextent.cx;
|
|
|
|
// Skip over the characters we just drew.
|
|
lpstring += cch;
|
|
|
|
// See if we have more to draw OR see if this string ends in
|
|
// a tab character that needs to be drawn.
|
|
if((nCount > 0) || ((nCount == 0) && (*lpstring == TEXT('\t'))))
|
|
{
|
|
|
|
lpstring++; // Skip over the tab
|
|
continue;
|
|
}
|
|
else
|
|
break; // Break from the loop.
|
|
}
|
|
return MAKELONG((x - initialx), (short)textextent.cy);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* TabbedTextOutW
|
|
*
|
|
* effects: Outputs the tabbed text and returns the
|
|
* textextent of the tabbed text.
|
|
*
|
|
* nCount Count of bytes in string
|
|
* nTabPositions Count of tabstops in tabstop array
|
|
* lpintTabStopPositions Tab stop positions in pixels
|
|
* iTabOrigin Tab stops are with respect to this
|
|
*
|
|
* History:
|
|
* 19-Jan-1993 mikeke Client side
|
|
\***************************************************************************/
|
|
|
|
LONG TabbedTextOutW(
|
|
HDC hdc,
|
|
int x,
|
|
int y,
|
|
LPCWSTR lpstring,
|
|
int cchChars,
|
|
int nTabPositions,
|
|
CONST INT *lpintTabStopPositions,
|
|
int iTabOrigin)
|
|
{
|
|
return TabTextOut(hdc, x, y, lpstring, cchChars,
|
|
nTabPositions, lpintTabStopPositions, iTabOrigin, TRUE, -1);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* TabbedTextOutA (API)
|
|
*
|
|
* History:
|
|
* 30-11-92 mikeke Created
|
|
\***************************************************************************/
|
|
|
|
LONG TabbedTextOutA(
|
|
HDC hdc,
|
|
int x,
|
|
int y,
|
|
LPCSTR pString,
|
|
int chCount,
|
|
int nTabPositions,
|
|
CONST INT *pnTabStopPositions,
|
|
int nTabOrigin)
|
|
{
|
|
LPWSTR lpwstr;
|
|
BOOL bRet;
|
|
WORD wCodePage = (WORD)GdiGetCodePage(hdc);
|
|
int iUniString;
|
|
|
|
if (chCount == -1) {
|
|
chCount = USER_AWCONV_COUNTSTRINGSZ;
|
|
}
|
|
|
|
if ((iUniString = MBToWCSEx(wCodePage, pString, chCount, &lpwstr, -1, TRUE)) == 0) {
|
|
if (chCount == USER_AWCONV_COUNTSTRINGSZ) {
|
|
lpwstr = (LPWSTR)gwszNullStr;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
bRet = TabTextOut(
|
|
hdc, x, y, lpwstr, iUniString, nTabPositions,
|
|
pnTabStopPositions, nTabOrigin, TRUE, GetTextCharset(hdc));
|
|
|
|
if (lpwstr != gwszNullStr) {
|
|
UserLocalFree((HANDLE)lpwstr);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
DWORD GetTabbedTextExtentW(
|
|
HDC hdc,
|
|
LPCWSTR pString,
|
|
int chCount,
|
|
int nTabPositions,
|
|
CONST INT *pnTabStopPositions)
|
|
{
|
|
return TabTextOut(hdc, 0, 0, pString, chCount,
|
|
nTabPositions, pnTabStopPositions, 0, FALSE, -1);
|
|
}
|
|
|
|
DWORD GetTabbedTextExtentA(
|
|
HDC hdc,
|
|
LPCSTR pString,
|
|
int chCount,
|
|
int nTabPositions,
|
|
CONST INT *pnTabStopPositions)
|
|
{
|
|
LPWSTR lpwstr;
|
|
BOOL bRet;
|
|
WORD wCodePage = (WORD)GdiGetCodePage(hdc);
|
|
int iUniString;
|
|
|
|
if (chCount == -1) {
|
|
chCount = USER_AWCONV_COUNTSTRINGSZ;
|
|
}
|
|
if ((iUniString = MBToWCSEx(wCodePage, pString, chCount, &lpwstr, -1, TRUE)) == 0) {
|
|
if (chCount == USER_AWCONV_COUNTSTRINGSZ) {
|
|
lpwstr = (LPWSTR)gwszNullStr;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
bRet = TabTextOut(hdc, 0, 0, lpwstr, iUniString,
|
|
nTabPositions, pnTabStopPositions, 0, FALSE, GetTextCharset(hdc));
|
|
|
|
if (lpwstr != gwszNullStr) {
|
|
UserLocalFree((HANDLE)lpwstr);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* PSMTextOut
|
|
*
|
|
* Outputs the text and puts and _ below the character with an &
|
|
* before it. Note that this routine isn't used for menus since menus
|
|
* have their own special one so that it is specialized and faster...
|
|
*
|
|
* History:
|
|
* 11-13-90 JimA Ported to NT.
|
|
* 30-Nov-1992 mikeke Client side version
|
|
* 7-Apr-1998 MCostea Added dwFlags
|
|
\***************************************************************************/
|
|
|
|
void PSMTextOut(
|
|
HDC hdc,
|
|
int xLeft,
|
|
int yTop,
|
|
LPWSTR lpsz,
|
|
int cch,
|
|
DWORD dwFlags)
|
|
{
|
|
/*
|
|
* By default this is just a call to UserLpkPSMTextOut. If an
|
|
* LPK is installed, this calls out to the LPK. The LPK calls
|
|
* UserLpkPSMTextOut, if necessary.
|
|
*/
|
|
(*fpLpkPSMTextOut)(hdc, xLeft, yTop, lpsz, cch, dwFlags);
|
|
return;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UserLpkPSMTextOut
|
|
*
|
|
* NOTE: A very similar routine (xxxPSMTextOut) exists on the kernel
|
|
* side in text.c. Any changes to this routine most likely need
|
|
* to be made in xxxPSMTextOut as well.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
FUNCLOGVOID6(LOG_GENERAL, DUMMYCALLINGTYPE, UserLpkPSMTextOut, HDC, hdc, int, xLeft, int, yTop, LPWSTR, lpsz, int, cch, DWORD, dwFlags)
|
|
void UserLpkPSMTextOut(
|
|
HDC hdc,
|
|
int xLeft,
|
|
int yTop,
|
|
LPWSTR lpsz,
|
|
int cch,
|
|
DWORD dwFlags)
|
|
{
|
|
int cx;
|
|
LONG textsize, result;
|
|
WCHAR achWorkBuffer[255];
|
|
WCHAR *pchOut = achWorkBuffer;
|
|
TEXTMETRICW textMetric;
|
|
SIZE size;
|
|
RECT rc;
|
|
COLORREF color;
|
|
|
|
if (cch > sizeof(achWorkBuffer)/sizeof(WCHAR)) {
|
|
pchOut = (WCHAR*)UserLocalAlloc(HEAP_ZERO_MEMORY, (cch+1) * sizeof(WCHAR));
|
|
if (pchOut == NULL)
|
|
return;
|
|
}
|
|
|
|
result = GetPrefixCount(lpsz, cch, pchOut, cch);
|
|
/*
|
|
* DT_PREFIXONLY is a new 5.0 option used when switching from keyboard cues off
|
|
* to on.
|
|
*/
|
|
if (!(dwFlags & DT_PREFIXONLY)) {
|
|
TextOutW(hdc, xLeft, yTop, pchOut, cch - HIWORD(result));
|
|
}
|
|
|
|
/*
|
|
* Any true prefix characters to underline?
|
|
*/
|
|
if (LOWORD(result) == 0xFFFF || dwFlags & DT_HIDEPREFIX) {
|
|
if (pchOut != achWorkBuffer)
|
|
UserLocalFree(pchOut);
|
|
return;
|
|
}
|
|
|
|
if (!GetTextMetricsW(hdc, &textMetric)) {
|
|
textMetric.tmOverhang = 0;
|
|
textMetric.tmAscent = 0;
|
|
}
|
|
|
|
/*
|
|
* For proportional fonts, find starting point of underline.
|
|
*/
|
|
if (LOWORD(result) != 0) {
|
|
|
|
/*
|
|
* How far in does underline start (if not at 0th byte.).
|
|
*/
|
|
GetTextExtentPointW(hdc, pchOut, LOWORD(result), &size);
|
|
xLeft += size.cx;
|
|
|
|
/*
|
|
* Adjust starting point of underline if not at first char and there is
|
|
* an overhang. (Italics or bold fonts.)
|
|
*/
|
|
xLeft = xLeft - textMetric.tmOverhang;
|
|
}
|
|
|
|
/*
|
|
* Adjust for proportional font when setting the length of the underline and
|
|
* height of text.
|
|
*/
|
|
GetTextExtentPointW(hdc, pchOut + LOWORD(result), 1, &size);
|
|
textsize = size.cx;
|
|
|
|
/*
|
|
* Find the width of the underline character. Just subtract out the overhang
|
|
* divided by two so that we look better with italic fonts. This is not
|
|
* going to effect embolded fonts since their overhang is 1.
|
|
*/
|
|
cx = LOWORD(textsize) - textMetric.tmOverhang / 2;
|
|
|
|
/*
|
|
* Get height of text so that underline is at bottom.
|
|
*/
|
|
yTop += textMetric.tmAscent + 1;
|
|
|
|
/*
|
|
* Draw the underline using the foreground color.
|
|
*/
|
|
SetRect(&rc, xLeft, yTop, xLeft+cx, yTop+1);
|
|
color = SetBkColor(hdc, GetTextColor(hdc));
|
|
ExtTextOutW(hdc, xLeft, yTop, ETO_OPAQUE, &rc, TEXT(""), 0, NULL);
|
|
SetBkColor(hdc, color);
|
|
|
|
if (pchOut != achWorkBuffer) {
|
|
UserLocalFree(pchOut);
|
|
}
|
|
}
|