reactos/win32ss/gdi/gdi32/objects/text.c
Baruch Rutman a4a59ad413 [GDI32][LPK] BiDi support for ExtTextOut and GetCharacterPlacement (#534)
Introduce BiDi (bi-directional text) support for ExtTextOut and GetCharacterPlacement, using Wine's GDI BIDI_Reorder function.
Solves the main issue with CORE-7003.

To be compatible with Win2k3+, introduce the "Language Pack" (LPK) dll.
- All the bidi code is removed from gdi32 and replaced by calls to LPK.
  Gdi32 uses dynamic linking to lpk.dll. In case of linking failure no bidi processing will be available.
- Implemented LpkGetCharacterPlacement.
- Implement LpkExtTextOut.
- Add a demo test program to show how the apis should function.
- Added all the remaining code, added special case for lpDx calculation if also GCP_GLYPHSHAPE flag was called.
  Applications that call GCP that use GCP_GLYPHSHAPE flags should also use the GCP_REORDER flag.
  (As written in https://msdn.microsoft.com/en-us/library/windows/desktop/dd144860(v=vs.85).aspx )
- Add ETO_RTLREADING flag handling.
  Imported the ETO_RTLREADING flag handling from wine, which changes the string part order (runs).
  A RRR1LLLRRR2 string without will show as RRR1LLLRRR2 without it, with it RRR2LLLRRR1.
2018-05-30 14:41:22 +02:00

930 lines
17 KiB
C

/*
* PROJECT: ReactOS GDI32
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* PURPOSE: Text drawing API.
* COPYRIGHT: Copyright 2014 Timo Kreuzer
* Copyright 2017 Katayama Hirofumi MZ
*/
#include <precomp.h>
#define NDEBUG
#include <debug.h>
/*
* @implemented
*/
BOOL
WINAPI
TextOutA(
_In_ HDC hdc,
_In_ INT nXStart,
_In_ INT nYStart,
_In_reads_(cchString) LPCSTR lpString,
_In_ INT cchString)
{
ANSI_STRING StringA;
UNICODE_STRING StringU;
BOOL bResult;
NTSTATUS Status;
if (lpString != NULL && cchString > 0)
{
if (cchString > MAXUSHORT)
cchString = MAXUSHORT;
StringA.Length = (USHORT)cchString;
StringA.MaximumLength = (USHORT)cchString;
StringA.Buffer = (PCHAR)lpString;
Status = RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
if (!NT_SUCCESS(Status))
{
StringU.Buffer = NULL;
StringU.Length = 0;
}
}
else
{
StringU.Buffer = NULL;
StringU.Length = 0;
}
bResult = TextOutW(hdc, nXStart, nYStart,
StringU.Buffer, StringU.Length / sizeof(WCHAR));
RtlFreeUnicodeString(&StringU);
return bResult;
}
/*
* @implemented
*/
BOOL
WINAPI
TextOutW(
_In_ HDC hdc,
_In_ INT nXStart,
_In_ INT nYStart,
_In_reads_(cchString) LPCWSTR lpString,
_In_ INT cchString)
{
return ExtTextOutW(hdc, nXStart, nYStart, 0, NULL, (LPWSTR)lpString, cchString, NULL);
}
/*
* @unimplemented
*/
BOOL
WINAPI
PolyTextOutA(
_In_ HDC hdc,
_In_reads_(cStrings) const POLYTEXTA *pptxt,
_In_ INT cStrings)
{
for (; cStrings>0; cStrings--, pptxt++)
{
if (!ExtTextOutA(hdc,
pptxt->x,
pptxt->y,
pptxt->uiFlags,
&pptxt->rcl,
pptxt->lpstr,
pptxt->n,
pptxt->pdx))
{
return FALSE;
}
}
return TRUE;
}
/*
* @unimplemented
*/
BOOL
WINAPI
PolyTextOutW(
_In_ HDC hdc,
_In_reads_(cStrings) const POLYTEXTW *pptxt,
_In_ INT cStrings)
{
for (; cStrings>0; cStrings--, pptxt++)
{
if (!ExtTextOutW(hdc,
pptxt->x,
pptxt->y,
pptxt->uiFlags,
&pptxt->rcl,
pptxt->lpstr,
pptxt->n,
pptxt->pdx))
{
return FALSE;
}
}
return TRUE;
}
/*
* @implemented
*/
DWORD
WINAPI
GdiGetCodePage(
_In_ HDC hdc)
{
PDC_ATTR pdcattr;
/* Get the DC attribute */
pdcattr = GdiGetDcAttr(hdc);
if (pdcattr == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if (pdcattr->ulDirty_ & DIRTY_CHARSET)
return LOWORD(NtGdiGetCharSet(hdc));
return LOWORD(pdcattr->iCS_CP);
}
/*
* @unimplemented
*/
INT
WINAPI
GetTextCharacterExtra(
_In_ HDC hdc)
{
PDC_ATTR pdcattr;
/* Get the DC attribute */
pdcattr = GdiGetDcAttr(hdc);
if (pdcattr == NULL)
{
/* Do not set LastError here! */
return 0x8000000;
}
return pdcattr->lTextExtra;
}
/*
* @implemented
*/
INT
WINAPI
GetTextCharset(
_In_ HDC hdc)
{
/* MSDN docs say this is equivalent */
return NtGdiGetTextCharsetInfo(hdc,NULL,0);
}
/*
* @implemented
*/
BOOL
WINAPI
GetTextMetricsA(
_In_ HDC hdc,
_Out_ LPTEXTMETRICA lptm)
{
TMW_INTERNAL tmwi;
if (!NtGdiGetTextMetricsW(hdc, &tmwi, sizeof(TMW_INTERNAL)))
{
return FALSE;
}
FONT_TextMetricWToA(&tmwi.TextMetric, lptm);
return TRUE;
}
/*
* @implemented
*/
BOOL
WINAPI
GetTextMetricsW(
_In_ HDC hdc,
_Out_ LPTEXTMETRICW lptm)
{
TMW_INTERNAL tmwi;
if (!NtGdiGetTextMetricsW(hdc, &tmwi, sizeof(TMW_INTERNAL)))
{
return FALSE;
}
*lptm = tmwi.TextMetric;
return TRUE;
}
/*
* @implemented
*/
BOOL
APIENTRY
GetTextExtentPointA(
_In_ HDC hdc,
_In_reads_(cchString) LPCSTR lpString,
_In_ INT cchString,
_Out_ LPSIZE lpsz)
{
ANSI_STRING StringA;
UNICODE_STRING StringU;
BOOL ret;
RtlInitAnsiString(&StringA, (LPSTR)lpString);
RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
ret = GetTextExtentPointW(hdc, StringU.Buffer, cchString, lpsz);
RtlFreeUnicodeString(&StringU);
return ret;
}
/*
* @implemented
*/
BOOL
APIENTRY
GetTextExtentPointW(
_In_ HDC hdc,
_In_reads_(cchString) LPCWSTR lpString,
_In_ INT cchString,
_Out_ LPSIZE lpsz)
{
return NtGdiGetTextExtent(hdc, (LPWSTR)lpString, cchString, lpsz, 0);
}
/*
* @implemented
*/
BOOL
WINAPI
GetTextExtentExPointW(
_In_ HDC hdc,
_In_reads_(cchString) LPCWSTR lpszString,
_In_ INT cchString,
_In_ INT nMaxExtent,
_Out_opt_ LPINT lpnFit,
_Out_writes_to_opt_(cchString, *lpnFit) LPINT lpnDx,
_Out_ LPSIZE lpSize)
{
/* Windows doesn't check nMaxExtent validity in unicode version */
if (nMaxExtent < -1)
{
DPRINT("nMaxExtent is invalid: %d\n", nMaxExtent);
}
return NtGdiGetTextExtentExW (
hdc, (LPWSTR)lpszString, cchString, nMaxExtent, (PULONG)lpnFit, (PULONG)lpnDx, lpSize, 0 );
}
/*
* @implemented
*/
BOOL
WINAPI
GetTextExtentExPointWPri(
_In_ HDC hdc,
_In_reads_(cwc) LPWSTR lpwsz,
_In_ ULONG cwc,
_In_ ULONG dxMax,
_Out_opt_ ULONG *pcCh,
_Out_writes_to_opt_(cwc, *pcCh) PULONG pdxOut,
_In_ LPSIZE psize)
{
return NtGdiGetTextExtentExW(hdc, lpwsz, cwc, dxMax, pcCh, pdxOut, psize, 0);
}
/*
* @implemented
*/
BOOL
WINAPI
GetTextExtentExPointA(
_In_ HDC hdc,
_In_reads_(cchString) LPCSTR lpszStr,
_In_ INT cchString,
_In_ INT nMaxExtent,
_Out_opt_ LPINT lpnFit,
_Out_writes_to_opt_ (cchString, *lpnFit) LPINT lpnDx,
_Out_ LPSIZE lpSize)
{
NTSTATUS Status;
LPWSTR lpszStrW;
BOOL bResult = FALSE;
if (nMaxExtent < -1)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
Status = HEAP_strdupA2W(&lpszStrW, lpszStr);
if (!NT_SUCCESS (Status))
{
SetLastError(RtlNtStatusToDosError(Status));
return FALSE;
}
bResult = NtGdiGetTextExtentExW(hdc,
lpszStrW,
cchString,
nMaxExtent,
(PULONG)lpnFit,
(PULONG)lpnDx,
lpSize,
0);
HEAP_free(lpszStrW);
return bResult;
}
/*
* @implemented
*/
BOOL
WINAPI
GetTextExtentPoint32A(
_In_ HDC hdc,
_In_reads_(cchString) LPCSTR lpString,
_In_ INT cchString,
_Out_ LPSIZE lpSize)
{
ANSI_STRING StringA;
UNICODE_STRING StringU;
BOOL ret;
StringA.Buffer = (LPSTR)lpString;
StringA.Length = cchString;
RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
ret = GetTextExtentPoint32W(hdc, StringU.Buffer, cchString, lpSize);
RtlFreeUnicodeString(&StringU);
return ret;
}
/*
* @implemented
*/
BOOL
WINAPI
GetTextExtentPoint32W(
_In_ HDC hdc,
_In_reads_(cchString) LPCWSTR lpString,
_In_ int cchString,
_Out_ LPSIZE lpSize)
{
return NtGdiGetTextExtent(hdc, (LPWSTR)lpString, cchString, lpSize, 0);
}
/*
* @implemented
*/
BOOL
WINAPI
GetTextExtentExPointI(
_In_ HDC hdc,
_In_reads_(cgi) LPWORD pgiIn,
_In_ INT cgi,
_In_ INT nMaxExtent,
_Out_opt_ LPINT lpnFit,
_Out_writes_to_opt_(cwchString, *lpnFit) LPINT lpnDx,
_Out_ LPSIZE lpSize)
{
return NtGdiGetTextExtentExW(hdc,
pgiIn,
cgi,
nMaxExtent,
(PULONG)lpnFit,
(PULONG)lpnDx,
lpSize,
GTEF_INDICES);
}
/*
* @implemented
*/
BOOL
WINAPI
GetTextExtentPointI(
_In_ HDC hdc,
_In_reads_(cgi) LPWORD pgiIn,
_In_ int cgi,
_Out_ LPSIZE lpSize)
{
return NtGdiGetTextExtent(hdc, pgiIn, cgi, lpSize, GTEF_INDICES);
}
/*
* @implemented
*/
BOOL
WINAPI
ExtTextOutA(
_In_ HDC hdc,
_In_ INT x,
_In_ INT y,
_In_ UINT fuOptions,
_In_opt_ const RECT *lprc,
_In_reads_opt_(cch) LPCSTR lpString,
_In_ UINT cch,
_In_reads_opt_(cch) const INT *lpDx)
{
ANSI_STRING StringA;
UNICODE_STRING StringU;
BOOL ret;
RtlInitAnsiString(&StringA, (LPSTR)lpString);
RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
ret = ExtTextOutW(hdc, x, y, fuOptions, lprc, StringU.Buffer, cch, lpDx);
RtlFreeUnicodeString(&StringU);
return ret;
}
/*
* @implemented
*/
BOOL
WINAPI
ExtTextOutW(
_In_ HDC hdc,
_In_ INT x,
_In_ INT y,
_In_ UINT fuOptions,
_In_opt_ const RECT *lprc,
_In_reads_opt_(cwc) LPCWSTR lpString,
_In_ UINT cwc,
_In_reads_opt_(cwc) const INT *lpDx)
{
HANDLE_METADC(BOOL,
ExtTextOut,
FALSE,
hdc,
x,
y,
fuOptions,
lprc,
lpString,
cwc,
lpDx);
if (!(fuOptions & (ETO_GLYPH_INDEX | ETO_IGNORELANGUAGE)))
{
if (LoadLPK(LPK_ETO))
return LpkExtTextOut(hdc, x, y, fuOptions, lprc, lpString, cwc , lpDx, 0);
}
return NtGdiExtTextOutW(hdc,
x,
y,
fuOptions,
(LPRECT)lprc,
(LPWSTR)lpString,
cwc,
(LPINT)lpDx,
0);
}
/*
* @implemented
*/
INT
WINAPI
GetTextFaceW(
_In_ HDC hdc,
_In_ INT cwcMax,
_Out_writes_to_opt_(cwcMax, return) LPWSTR pFaceName)
{
/* Validate parameters */
if (pFaceName && cwcMax <= 0)
{
/* Set last error and return failure */
GdiSetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
/* Forward to kernel */
return NtGdiGetTextFaceW(hdc, cwcMax, pFaceName, FALSE);
}
/*
* @implemented
*/
INT
WINAPI
GetTextFaceA(
_In_ HDC hdc,
_In_ INT cchMax,
_Out_writes_to_opt_(cchMax, return) LPSTR lpName)
{
INT res;
LPWSTR nameW;
/* Validate parameters */
if (lpName && cchMax <= 0)
{
/* Set last error and return failure */
GdiSetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
res = GetTextFaceW(hdc, 0, NULL);
nameW = HeapAlloc( GetProcessHeap(), 0, res * 2 );
if (nameW == NULL)
{
return 0;
}
GetTextFaceW( hdc, res, nameW );
if (lpName)
{
if (cchMax && !WideCharToMultiByte( CP_ACP, 0, nameW, -1, lpName, cchMax, NULL, NULL))
lpName[cchMax-1] = 0;
res = strlen(lpName);
}
else
{
res = WideCharToMultiByte( CP_ACP, 0, nameW, -1, NULL, 0, NULL, NULL);
}
HeapFree( GetProcessHeap(), 0, nameW );
return res;
}
/*
* @implemented
*/
INT
WINAPI
GetTextFaceAliasW(
_In_ HDC hdc,
_In_ INT cwcMax,
_Out_writes_to_opt_(cwcMax, return) LPWSTR pszOut)
{
if (pszOut && !cwcMax)
{
GdiSetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
return NtGdiGetTextFaceW(hdc, cwcMax, pszOut, TRUE);
}
BOOL
WINAPI
GetFontResourceInfoW(
_In_z_ LPCWSTR lpFileName,
_Inout_ DWORD *pdwBufSize,
_Out_writes_to_opt_(*pdwBufSize, 1) PVOID lpBuffer,
_In_ DWORD dwType)
{
BOOL bRet;
UNICODE_STRING NtFileName;
DPRINT("GetFontResourceInfoW: dwType = %lu\n", dwType);
if (!lpFileName || !pdwBufSize)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!RtlDosPathNameToNtPathName_U(lpFileName,
&NtFileName,
NULL,
NULL))
{
SetLastError(ERROR_PATH_NOT_FOUND);
return FALSE;
}
bRet = NtGdiGetFontResourceInfoInternalW(
NtFileName.Buffer,
(NtFileName.Length / sizeof(WCHAR)) + 1,
1,
*pdwBufSize,
pdwBufSize,
lpBuffer,
dwType);
RtlFreeHeap(RtlGetProcessHeap(), 0, NtFileName.Buffer);
return bRet;
}
/*
* @unimplemented
*/
INT
WINAPI
SetTextCharacterExtra(
_In_ HDC hdc,
_In_ INT nCharExtra)
{
PDC_ATTR pdcattr;
INT nOldCharExtra;
if (nCharExtra == 0x80000000)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0x80000000;
}
if (GDI_HANDLE_GET_TYPE(hdc) == GDILoObjType_LO_METADC16_TYPE)
{
HANDLE_METADC(INT, SetTextCharacterExtra, 0x80000000, hdc, nCharExtra);
}
/* Get the DC attribute */
pdcattr = GdiGetDcAttr(hdc);
if (pdcattr == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0x8000000;
}
if (NtCurrentTeb()->GdiTebBatch.HDC == hdc)
{
if (pdcattr->ulDirty_ & DC_FONTTEXT_DIRTY)
{
NtGdiFlush(); // Sync up pdcattr from Kernel space.
pdcattr->ulDirty_ &= ~(DC_MODE_DIRTY|DC_FONTTEXT_DIRTY);
}
}
nOldCharExtra = pdcattr->lTextExtra;
pdcattr->lTextExtra = nCharExtra;
return nOldCharExtra;
}
/*
* @implemented
*
*/
UINT
WINAPI
GetTextAlign(
_In_ HDC hdc)
{
PDC_ATTR pdcattr;
/* Get the DC attribute */
pdcattr = GdiGetDcAttr(hdc);
if (pdcattr == NULL)
{
/* Do not set LastError here! */
return GDI_ERROR;
}
return pdcattr->lTextAlign;
}
/*
* @implemented
*
*/
COLORREF
WINAPI
GetTextColor(
_In_ HDC hdc)
{
PDC_ATTR pdcattr;
/* Get the DC attribute */
pdcattr = GdiGetDcAttr(hdc);
if (pdcattr == NULL)
{
/* Do not set LastError here! */
return CLR_INVALID;
}
return pdcattr->ulForegroundClr;
}
/*
* @unimplemented
*/
UINT
WINAPI
SetTextAlign(
_In_ HDC hdc,
_In_ UINT fMode)
{
PDC_ATTR pdcattr;
UINT fOldMode;
HANDLE_METADC(BOOL, SetTextAlign, GDI_ERROR, hdc, fMode);
/* Get the DC attribute */
pdcattr = GdiGetDcAttr(hdc);
if (pdcattr == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return GDI_ERROR;
}
fOldMode = pdcattr->lTextAlign;
pdcattr->lTextAlign = fMode; // Raw
if (pdcattr->dwLayout & LAYOUT_RTL)
{
if ((fMode & TA_CENTER) != TA_CENTER) fMode ^= TA_RIGHT;
}
pdcattr->flTextAlign = fMode & TA_MASK;
return fOldMode;
}
/*
* @implemented
*/
COLORREF
WINAPI
SetTextColor(
_In_ HDC hdc,
_In_ COLORREF crColor)
{
PDC_ATTR pdcattr;
COLORREF crOldColor;
HANDLE_METADC(COLORREF, SetTextColor, CLR_INVALID, hdc, crColor);
pdcattr = GdiGetDcAttr(hdc);
if (pdcattr == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return CLR_INVALID;
}
crOldColor = (COLORREF) pdcattr->ulForegroundClr;
pdcattr->ulForegroundClr = (ULONG)crColor;
if (pdcattr->crForegroundClr != crColor)
{
pdcattr->ulDirty_ |= (DIRTY_TEXT|DIRTY_LINE|DIRTY_FILL);
pdcattr->crForegroundClr = crColor;
}
return crOldColor;
}
/*
* @implemented
*/
BOOL
WINAPI
SetTextJustification(
_In_ HDC hdc,
_In_ INT nBreakExtra,
_In_ INT nBreakCount)
{
PDC_ATTR pdcattr;
if (GDI_HANDLE_GET_TYPE(hdc) == GDILoObjType_LO_METADC16_TYPE)
{
HANDLE_METADC(BOOL, SetTextJustification, FALSE, hdc, nBreakExtra, nBreakCount);
}
/* Get the DC attribute */
pdcattr = GdiGetDcAttr(hdc);
if (pdcattr == NULL)
{
/* Do not set LastError here! */
return GDI_ERROR;
}
if (NtCurrentTeb()->GdiTebBatch.HDC == hdc)
{
if (pdcattr->ulDirty_ & DC_FONTTEXT_DIRTY)
{
NtGdiFlush(); // Sync up pdcattr from Kernel space.
pdcattr->ulDirty_ &= ~(DC_MODE_DIRTY|DC_FONTTEXT_DIRTY);
}
}
pdcattr->cBreak = nBreakCount;
pdcattr->lBreakExtra = nBreakExtra;
return TRUE;
}
/*
* @implemented
*/
UINT
WINAPI
GetStringBitmapA(
_In_ HDC hdc,
_In_ LPSTR psz,
_In_ BOOL bDoCall,
_In_ UINT cj,
_Out_writes_(cj) BYTE *lpSB)
{
NTSTATUS Status;
PWSTR pwsz;
UINT uResult = 0;
if (!bDoCall)
{
return 0;
}
Status = HEAP_strdupA2W(&pwsz, psz);
if (!NT_SUCCESS(Status))
{
SetLastError (RtlNtStatusToDosError(Status));
}
else
{
uResult = NtGdiGetStringBitmapW(hdc, pwsz, 1, lpSB, cj);
HEAP_free(pwsz);
}
return uResult;
}
/*
* @implemented
*/
UINT
WINAPI
GetStringBitmapW(
_In_ HDC hdc,
_In_ LPWSTR pwsz,
_In_ BOOL bDoCall,
_In_ UINT cj,
_Out_writes_(cj) BYTE *lpSB)
{
if (!bDoCall)
{
return 0;
}
return NtGdiGetStringBitmapW(hdc, pwsz, 1, lpSB, cj);
}
/*
* @implemented
*/
BOOL
WINAPI
GetETM(
_In_ HDC hdc,
_Out_ EXTTEXTMETRIC *petm)
{
BOOL bResult;
bResult = NtGdiGetETM(hdc, petm);
if (bResult && petm)
{
petm->emKernPairs = (WORD)GetKerningPairsA(hdc, 0, 0);
}
return bResult;
}