reactos/win32ss/user/user32/windows/font.c
Baruch Rutman 87c529a537
[LPK] Small fix and a related comment. (#890)
[LPK][USER32] Fix reading layout checks.

- Both functions don't care about DT_RTLREADING flag.
- Added a different method to check reading order.

[USER32] Remove gcc-build breaker semicolon.
2018-10-08 16:17:42 +02:00

498 lines
14 KiB
C

/*
* ReactOS kernel
* Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* PROJECT: ReactOS user32.dll
* FILE: win32ss/user/user32/windows/font.c
* PURPOSE: Draw Text
* PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
* UPDATE HISTORY:
* 09-05-2001 CSH Created
*/
/* INCLUDES ******************************************************************/
#include <user32.h>
DWORD WINAPI GdiGetCodePage(HDC hdc);
INT WINAPI DrawTextExWorker( HDC hdc, LPWSTR str, INT i_count,
LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp );
/* FUNCTIONS *****************************************************************/
/***********************************************************************
* TEXT_TabbedTextOut
*
* Helper function for TabbedTextOut() and GetTabbedTextExtent().
* Note: this doesn't work too well for text-alignment modes other
* than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
*/
/* WINE synced 22-May-2006 */
LONG TEXT_TabbedTextOut( HDC hdc,
INT x,
INT y,
LPCWSTR lpstr,
INT count,
INT cTabStops,
const INT *lpTabPos,
INT nTabOrg,
BOOL fDisplayText )
{
INT defWidth;
SIZE extent;
int i, j;
int start = x;
TEXTMETRICW tm;
if (!lpTabPos)
cTabStops=0;
GetTextMetricsW( hdc, &tm );
if (cTabStops == 1)
{
defWidth = *lpTabPos;
cTabStops = 0;
}
else
{
defWidth = 8 * tm.tmAveCharWidth;
}
while (count > 0)
{
RECT r;
INT x0;
x0 = x;
r.left = x0;
/* chop the string into substrings of 0 or more <tabs>
* possibly followed by 1 or more normal characters */
for (i = 0; i < count; i++)
if (lpstr[i] != '\t') break;
for (j = i; j < count; j++)
if (lpstr[j] == '\t') break;
/* get the extent of the normal character part */
GetTextExtentPointW( hdc, lpstr + i, j - i , &extent );
/* and if there is a <tab>, calculate its position */
if( i) {
/* get x coordinate for the drawing of this string */
for (; cTabStops > i; lpTabPos++, cTabStops--)
{
if( nTabOrg + abs( *lpTabPos) > x) {
if( lpTabPos[ i - 1] >= 0) {
/* a left aligned tab */
x = nTabOrg + lpTabPos[ i-1] + extent.cx;
break;
}
else
{
/* if tab pos is negative then text is right-aligned
* to tab stop meaning that the string extends to the
* left, so we must subtract the width of the string */
if (nTabOrg - lpTabPos[ i - 1] - extent.cx > x)
{
x = nTabOrg - lpTabPos[ i - 1];
x0 = x - extent.cx;
break;
}
}
}
}
/* if we have run out of tab stops and we have a valid default tab
* stop width then round x up to that width */
if ((cTabStops <= i) && (defWidth > 0)) {
x0 = nTabOrg + ((x - nTabOrg) / defWidth + i) * defWidth;
x = x0 + extent.cx;
} else if ((cTabStops <= i) && (defWidth < 0)) {
x = nTabOrg + ((x - nTabOrg + extent.cx) / -defWidth + i)
* -defWidth;
x0 = x - extent.cx;
}
} else
x += extent.cx;
if (fDisplayText)
{
r.top = y;
r.right = x;
r.bottom = y + extent.cy;
ExtTextOutW( hdc, x0, y, GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0,
&r, lpstr + i, j - i, NULL );
}
count -= j;
lpstr += j;
}
if(!extent.cy)
extent.cy = tm.tmHeight;
return MAKELONG(x - start, extent.cy);
}
/*
* @implemented
*/
LONG
WINAPI
TabbedTextOutA(
HDC hDC,
int X,
int Y,
LPCSTR lpString,
int nCount,
int nTabPositions,
CONST INT *lpnTabStopPositions,
int nTabOrigin)
{
LONG ret;
DWORD len;
LPWSTR strW;
UINT cp = GdiGetCodePage( hDC ); // CP_ACP
len = MultiByteToWideChar(cp, 0, lpString, nCount, NULL, 0);
if (!len) return 0;
strW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
if (!strW) return 0;
MultiByteToWideChar(cp, 0, lpString, nCount, strW, len);
ret = TabbedTextOutW(hDC, X, Y, strW, len, nTabPositions, lpnTabStopPositions, nTabOrigin);
HeapFree(GetProcessHeap(), 0, strW);
return ret;
}
/*
* @implemented
*/
LONG
WINAPI
TabbedTextOutW(
HDC hDC,
int X,
int Y,
LPCWSTR lpString,
int nCount,
int nTabPositions,
CONST INT *lpnTabStopPositions,
int nTabOrigin)
{
return TEXT_TabbedTextOut(hDC, X, Y, lpString, nCount, nTabPositions, lpnTabStopPositions, nTabOrigin, TRUE);
}
/* WINE synced 22-May-2006 */
/*
* @implemented
*/
DWORD
WINAPI
GetTabbedTextExtentA(
HDC hDC,
LPCSTR lpString,
int nCount,
int nTabPositions,
CONST INT *lpnTabStopPositions)
{
LONG ret;
UINT cp = GdiGetCodePage( hDC ); // CP_ACP
DWORD len;
LPWSTR strW;
len = MultiByteToWideChar(cp, 0, lpString, nCount, NULL, 0);
if (!len) return 0;
strW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
if (!strW) return 0;
MultiByteToWideChar(cp, 0, lpString, nCount, strW, len);
ret = GetTabbedTextExtentW(hDC, strW, len, nTabPositions, lpnTabStopPositions);
HeapFree(GetProcessHeap(), 0, strW);
return ret;
}
/*
* @implemented
*/
DWORD
WINAPI
GetTabbedTextExtentW(
HDC hDC,
LPCWSTR lpString,
int nCount,
int nTabPositions,
CONST INT *lpnTabStopPositions)
{
return TEXT_TabbedTextOut(hDC, 0, 0, lpString, nCount, nTabPositions, lpnTabStopPositions, 0, FALSE);
}
/***********************************************************************
* DrawTextExW (USER32.@)
*
* The documentation on the extra space required for DT_MODIFYSTRING at MSDN
* is not quite complete, especially with regard to \0. We will assume that
* the returned string could have a length of up to i_count+3 and also have
* a trailing \0 (which would be 4 more than a not-null-terminated string but
* 3 more than a null-terminated string). If this is not so then increase
* the allowance in DrawTextExA.
*/
#define MAX_BUFFER 1024
/*
* @implemented
*
* Synced with Wine Staging 1.7.37
*/
INT WINAPI DrawTextExW( HDC hdc, LPWSTR str, INT i_count,
LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
{
return DrawTextExWorker( hdc, str, i_count, rect, flags, dtp );
}
/***********************************************************************
* DrawTextExA (USER32.@)
*
* If DT_MODIFYSTRING is specified then there must be room for up to
* 4 extra characters. We take great care about just how much modified
* string we return.
*
* @implemented
*
* Synced with Wine Staging 1.7.37
*/
INT WINAPI DrawTextExA( HDC hdc, LPSTR str, INT count,
LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
{
WCHAR *wstr;
WCHAR *p;
INT ret = 0;
int i;
DWORD wcount;
DWORD wmax;
DWORD amax;
UINT cp;
if (!count) return 0;
if (!str && count > 0) return 0;
if( !str || ((count == -1) && !(count = strlen(str))))
{
int lh;
TEXTMETRICA tm;
if (dtp && dtp->cbSize != sizeof(DRAWTEXTPARAMS))
return 0;
GetTextMetricsA(hdc, &tm);
if (flags & DT_EXTERNALLEADING)
lh = tm.tmHeight + tm.tmExternalLeading;
else
lh = tm.tmHeight;
if( flags & DT_CALCRECT)
{
rect->right = rect->left;
if( flags & DT_SINGLELINE)
rect->bottom = rect->top + lh;
else
rect->bottom = rect->top;
}
return lh;
}
cp = GdiGetCodePage( hdc );
wcount = MultiByteToWideChar( cp, 0, str, count, NULL, 0 );
wmax = wcount;
amax = count;
if (flags & DT_MODIFYSTRING)
{
wmax += 4;
amax += 4;
}
wstr = HeapAlloc(GetProcessHeap(), 0, wmax * sizeof(WCHAR));
if (wstr)
{
MultiByteToWideChar( cp, 0, str, count, wstr, wcount );
if (flags & DT_MODIFYSTRING)
for (i=4, p=wstr+wcount; i--; p++) *p=0xFFFE;
/* Initialise the extra characters so that we can see which ones
* change. U+FFFE is guaranteed to be not a unicode character and
* so will not be generated by DrawTextEx itself.
*/
ret = DrawTextExW( hdc, wstr, wcount, rect, flags, dtp );
if (flags & DT_MODIFYSTRING)
{
/* Unfortunately the returned string may contain multiple \0s
* and so we need to measure it ourselves.
*/
for (i=4, p=wstr+wcount; i-- && *p != 0xFFFE; p++) wcount++;
WideCharToMultiByte( cp, 0, wstr, wcount, str, amax, NULL, NULL );
}
HeapFree(GetProcessHeap(), 0, wstr);
}
return ret;
}
/***********************************************************************
* DrawTextW (USER32.@)
*
* @implemented
* Synced with Wine Staging 1.7.37
*/
INT WINAPI DrawTextW( HDC hdc, LPCWSTR str, INT count, LPRECT rect, UINT flags )
{
DRAWTEXTPARAMS dtp;
memset (&dtp, 0, sizeof(dtp));
dtp.cbSize = sizeof(dtp);
if (flags & DT_TABSTOP)
{
dtp.iTabLength = (flags >> 8) & 0xff;
flags &= 0xffff00ff;
}
return DrawTextExW(hdc, (LPWSTR)str, count, rect, flags, &dtp);
}
/***********************************************************************
* DrawTextA (USER32.@)
*
* @implemented
* Synced with Wine Staging 1.7.37
*/
INT WINAPI DrawTextA( HDC hdc, LPCSTR str, INT count, LPRECT rect, UINT flags )
{
DRAWTEXTPARAMS dtp;
memset (&dtp, 0, sizeof(dtp));
dtp.cbSize = sizeof(dtp);
if (flags & DT_TABSTOP)
{
dtp.iTabLength = (flags >> 8) & 0xff;
flags &= 0xffff00ff;
}
return DrawTextExA( hdc, (LPSTR)str, count, rect, flags, &dtp );
}
/***************************************************************************
* UserLpkPSMTextOut
*
* Stripped down version of DrawText, can only draw single line text and
* Prefix underscore (only on the last found amperstand)
* only flags to be found to be of use in testing:
*
* DT_NOPREFIX - Draw the string as is it without any changes
* DT_HIDEPREFIX - Draw the string without underscore
* DT_PREFIXONLY - Draw only the underscore
*
* without any of these flags the behavior is the string being drawn without the amperstands and
* with the underscore.
*
* lpk has an equivalent function - LpkPSMTextOut
* Notes by testing:
* This function in windows doesn't check if lpString is NULL, which results a crash,
* returns seemingly random values without any logic, and ignores the DT_NOPREFIX value.
* All of these issues don't exist in the LPK version.
*
* Note: lpString does not need to be null terminated
*/
#define PREFIX 38
#define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
#define KANA_PREFIX 31 /* Win16: Katakana prefix */
INT WINAPI UserLpkPSMTextOut(HDC hdc, int x, int y, LPCWSTR lpString, int cString, DWORD dwFlags)
{
SIZE size;
TEXTMETRICW tm;
int len, i = 0, j = 0;
int prefix_count = 0, prefix_offset = -1;
LPWSTR display_str = NULL;
int prefix_x, prefix_end;
HPEN hpen;
HPEN oldPen;
if (!lpString || cString <= 0)
return 0;
if (dwFlags & DT_NOPREFIX) /* Windows ignores this */
{
TextOutW(hdc, x, y, lpString, cString);
GetTextExtentPointW(hdc, lpString, cString, &size);
return size.cx;
}
display_str = HeapAlloc(GetProcessHeap(), 0, (cString + 1) * sizeof(WCHAR));
if (!display_str)
return 0;
while (i < cString)
{
if (lpString[i] == PREFIX || (iswspace(lpString[i]) && lpString[i] != L' '))
{
if (i < cString - 1 && lpString[i + 1] == PREFIX)
display_str[j++] = lpString[i++];
else
i++;
}
else
{
display_str[j++] = lpString[i++];
}
}
display_str[j] = L'\0';
len = wcslen(display_str);
if (!(dwFlags & DT_PREFIXONLY))
TextOutW(hdc, x, y, display_str, len);
if (!(dwFlags & DT_HIDEPREFIX))
{
for (i = 0; i < cString - 1; i++)
{
if (lpString[i] == PREFIX && lpString[i + 1] != PREFIX)
{
prefix_offset = i - prefix_count;
prefix_count++;
}
else if (lpString[i] == PREFIX && lpString[i + 1] == PREFIX)
{
i++;
}
}
GetTextMetricsW(hdc, &tm);
if (prefix_offset != -1)
{
GetTextExtentPointW(hdc, display_str, prefix_offset, &size);
prefix_x = x + size.cx;
GetTextExtentPointW(hdc, display_str, prefix_offset + 1, &size);
prefix_end = x + size.cx - 1;
hpen = CreatePen(PS_SOLID, 1, GetTextColor(hdc));
oldPen = SelectObject(hdc, hpen);
MoveToEx(hdc, prefix_x, y + tm.tmAscent + 1, NULL);
LineTo(hdc, prefix_end, y + tm.tmAscent + 1);
SelectObject(hdc, oldPen);
DeleteObject(hpen);
}
}
GetTextExtentPointW(hdc, display_str, len + 1, &size);
HeapFree(GetProcessHeap(), 0, display_str);
return size.cx;
}