2 * PROJECT: ReactOS GDI32
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Text drawing API.
5 * COPYRIGHT: Copyright 2014 Timo Kreuzer
6 * Copyright 2017 Katayama Hirofumi MZ
23 _In_reads_(cchString
) LPCSTR lpString
,
27 UNICODE_STRING StringU
;
31 if (lpString
!= NULL
&& cchString
> 0)
33 if (cchString
> MAXUSHORT
)
34 cchString
= MAXUSHORT
;
36 StringA
.Length
= (USHORT
)cchString
;
37 StringA
.MaximumLength
= (USHORT
)cchString
;
38 StringA
.Buffer
= (PCHAR
)lpString
;
40 Status
= RtlAnsiStringToUnicodeString(&StringU
, &StringA
, TRUE
);
41 if (!NT_SUCCESS(Status
))
43 StringU
.Buffer
= NULL
;
49 StringU
.Buffer
= NULL
;
53 bResult
= TextOutW(hdc
, nXStart
, nYStart
,
54 StringU
.Buffer
, StringU
.Length
/ sizeof(WCHAR
));
56 RtlFreeUnicodeString(&StringU
);
70 _In_reads_(cchString
) LPCWSTR lpString
,
73 return ExtTextOutW(hdc
, nXStart
, nYStart
, 0, NULL
, (LPWSTR
)lpString
, cchString
, NULL
);
84 _In_reads_(cStrings
) const POLYTEXTA
*pptxt
,
87 for (; cStrings
>0; cStrings
--, pptxt
++)
113 _In_reads_(cStrings
) const POLYTEXTW
*pptxt
,
116 for (; cStrings
>0; cStrings
--, pptxt
++)
118 if (!ExtTextOutW(hdc
,
145 /* Get the DC attribute */
146 pdcattr
= GdiGetDcAttr(hdc
);
149 SetLastError(ERROR_INVALID_PARAMETER
);
153 if (pdcattr
->ulDirty_
& DIRTY_CHARSET
)
154 return LOWORD(NtGdiGetCharSet(hdc
));
156 return LOWORD(pdcattr
->iCS_CP
);
165 GetTextCharacterExtra(
170 /* Get the DC attribute */
171 pdcattr
= GdiGetDcAttr(hdc
);
174 /* Do not set LastError here! */
178 return pdcattr
->lTextExtra
;
190 /* MSDN docs say this is equivalent */
191 return NtGdiGetTextCharsetInfo(hdc
,NULL
,0);
202 _Out_ LPTEXTMETRICA lptm
)
206 if (!NtGdiGetTextMetricsW(hdc
, &tmwi
, sizeof(TMW_INTERNAL
)))
211 FONT_TextMetricWToA(&tmwi
.TextMetric
, lptm
);
223 _Out_ LPTEXTMETRICW lptm
)
227 if (!NtGdiGetTextMetricsW(hdc
, &tmwi
, sizeof(TMW_INTERNAL
)))
232 *lptm
= tmwi
.TextMetric
;
244 _In_reads_(cchString
) LPCSTR lpString
,
249 UNICODE_STRING StringU
;
252 RtlInitAnsiString(&StringA
, (LPSTR
)lpString
);
253 RtlAnsiStringToUnicodeString(&StringU
, &StringA
, TRUE
);
255 ret
= GetTextExtentPointW(hdc
, StringU
.Buffer
, cchString
, lpsz
);
257 RtlFreeUnicodeString(&StringU
);
270 _In_reads_(cchString
) LPCWSTR lpString
,
274 return NtGdiGetTextExtent(hdc
, (LPWSTR
)lpString
, cchString
, lpsz
, 0);
283 GetTextExtentExPointW(
285 _In_reads_(cchString
) LPCWSTR lpszString
,
288 _Out_opt_ LPINT lpnFit
,
289 _Out_writes_to_opt_(cchString
, *lpnFit
) LPINT lpnDx
,
293 /* Windows doesn't check nMaxExtent validity in unicode version */
296 DPRINT("nMaxExtent is invalid: %d\n", nMaxExtent
);
299 if (LoadLPK(LPK_GTEP
))
300 return LpkGetTextExtentExPoint(hdc
, lpszString
, cchString
, nMaxExtent
, lpnFit
, lpnDx
, lpSize
, 0, 0);
302 return NtGdiGetTextExtentExW (
303 hdc
, (LPWSTR
)lpszString
, cchString
, nMaxExtent
, (PULONG
)lpnFit
, (PULONG
)lpnDx
, lpSize
, 0 );
312 GetTextExtentExPointWPri(
314 _In_reads_(cwc
) LPCWSTR lpwsz
,
317 _Out_opt_ LPINT pcCh
,
318 _Out_writes_to_opt_(cwc
, *pcCh
) LPINT pdxOut
,
321 return NtGdiGetTextExtentExW(hdc
, (LPWSTR
)lpwsz
, cwc
, dxMax
, (PULONG
)pcCh
, (PULONG
)pdxOut
, psize
, 0);
329 GetTextExtentExPointA(
331 _In_reads_(cchString
) LPCSTR lpszStr
,
334 _Out_opt_ LPINT lpnFit
,
335 _Out_writes_to_opt_ (cchString
, *lpnFit
) LPINT lpnDx
,
340 BOOL bResult
= FALSE
;
344 SetLastError(ERROR_INVALID_PARAMETER
);
348 Status
= HEAP_strdupA2W(&lpszStrW
, lpszStr
);
349 if (!NT_SUCCESS (Status
))
351 SetLastError(RtlNtStatusToDosError(Status
));
355 bResult
= NtGdiGetTextExtentExW(hdc
,
375 GetTextExtentPoint32A(
377 _In_reads_(cchString
) LPCSTR lpString
,
382 UNICODE_STRING StringU
;
385 StringA
.Buffer
= (LPSTR
)lpString
;
386 StringA
.Length
= cchString
;
387 RtlAnsiStringToUnicodeString(&StringU
, &StringA
, TRUE
);
389 ret
= GetTextExtentPoint32W(hdc
, StringU
.Buffer
, cchString
, lpSize
);
391 RtlFreeUnicodeString(&StringU
);
402 GetTextExtentPoint32W(
404 _In_reads_(cchString
) LPCWSTR lpString
,
408 return NtGdiGetTextExtent(hdc
, (LPWSTR
)lpString
, cchString
, lpSize
, 0);
416 GetTextExtentExPointI(
418 _In_reads_(cgi
) LPWORD pgiIn
,
421 _Out_opt_ LPINT lpnFit
,
422 _Out_writes_to_opt_(cwchString
, *lpnFit
) LPINT lpnDx
,
425 return NtGdiGetTextExtentExW(hdc
,
442 _In_reads_(cgi
) LPWORD pgiIn
,
446 return NtGdiGetTextExtent(hdc
, pgiIn
, cgi
, lpSize
, GTEF_INDICES
);
459 _In_opt_
const RECT
*lprc
,
460 _In_reads_opt_(cch
) LPCSTR lpString
,
462 _In_reads_opt_(cch
) const INT
*lpDx
)
465 UNICODE_STRING StringU
;
468 RtlInitAnsiString(&StringA
, (LPSTR
)lpString
);
469 RtlAnsiStringToUnicodeString(&StringU
, &StringA
, TRUE
);
471 ret
= ExtTextOutW(hdc
, x
, y
, fuOptions
, lprc
, StringU
.Buffer
, cch
, lpDx
);
473 RtlFreeUnicodeString(&StringU
);
489 _In_opt_
const RECT
*lprc
,
490 _In_reads_opt_(cwc
) LPCWSTR lpString
,
492 _In_reads_opt_(cwc
) const INT
*lpDx
)
508 if (!(fuOptions
& (ETO_GLYPH_INDEX
| ETO_IGNORELANGUAGE
)))
510 if (LoadLPK(LPK_ETO
))
511 return LpkExtTextOut(hdc
, x
, y
, fuOptions
, lprc
, lpString
, cwc
, lpDx
, 0);
514 /* Get the DC attribute */
515 pdcattr
= GdiGetDcAttr(hdc
);
517 !(pdcattr
->ulDirty_
& DC_DIBSECTION
) &&
518 !(pdcattr
->lTextAlign
& TA_UPDATECP
))
522 if ( fuOptions
& ETO_OPAQUE
)
524 PGDIBSEXTTEXTOUT pgO
;
526 pgO
= GdiAllocBatchCommand(hdc
, GdiBCExtTextOut
);
531 pgO
->Options
= fuOptions
;
532 /* Snapshot attribute */
533 pgO
->ulBackgroundClr
= pdcattr
->ulBackgroundClr
;
534 pgO
->ptlViewportOrg
= pdcattr
->ptlViewportOrg
;
538 else // Do nothing, old explorer pops this off.
540 DPRINT1("GdiBCExtTextOut nothing\n");
543 } // Max 580 wchars, if offset 0
544 else if ( cwc
<= ((GDIBATCHBUFSIZE
- sizeof(GDIBSTEXTOUT
)) / sizeof(WCHAR
)) )
547 PTEB pTeb
= NtCurrentTeb();
549 pgO
= GdiAllocBatchCommand(hdc
, GdiBCTextOut
);
555 if (cwc
> 2) cjSize
= (cwc
* sizeof(WCHAR
)) - sizeof(pgO
->String
);
557 /* Calculate buffer size for string and Dx values */
560 /* If ETO_PDY is specified, we have pairs of INTs */
561 DxSize
= (cwc
* sizeof(INT
)) * (fuOptions
& ETO_PDY
? 2 : 1);
563 // The structure buffer holds 4 bytes. Store Dx data then string.
564 // Result one wchar -> Buf[ Dx ]Str[wC], [4][2][X] one extra unused wchar
565 // to assure alignment of 4.
568 if ((pTeb
->GdiTebBatch
.Offset
+ cjSize
) <= GDIBATCHBUFSIZE
)
573 pgO
->Options
= fuOptions
;
576 if (lprc
) pgO
->Rect
= *lprc
;
579 pgO
->Options
|= GDIBS_NORECT
; // Tell the other side lprc is nill.
582 /* Snapshot attributes */
583 pgO
->crForegroundClr
= pdcattr
->crForegroundClr
;
584 pgO
->crBackgroundClr
= pdcattr
->crBackgroundClr
;
585 pgO
->ulForegroundClr
= pdcattr
->ulForegroundClr
;
586 pgO
->ulBackgroundClr
= pdcattr
->ulBackgroundClr
;
587 pgO
->lBkMode
= pdcattr
->lBkMode
== OPAQUE
? OPAQUE
: TRANSPARENT
;
588 pgO
->hlfntNew
= pdcattr
->hlfntNew
;
589 pgO
->flTextAlign
= pdcattr
->flTextAlign
;
590 pgO
->ptlViewportOrg
= pdcattr
->ptlViewportOrg
;
592 pgO
->Size
= DxSize
; // of lpDx then string after.
593 /* Put the Dx before the String to assure alignment of 4 */
594 if (lpDx
) RtlCopyMemory( &pgO
->Buffer
, lpDx
, DxSize
);
596 if (cwc
) RtlCopyMemory( &pgO
->String
[DxSize
/sizeof(WCHAR
)], lpString
, cwc
* sizeof(WCHAR
));
598 // Recompute offset and return size
599 pTeb
->GdiTebBatch
.Offset
+= cjSize
;
600 ((PGDIBATCHHDR
)pgO
)->Size
+= cjSize
;
603 // Reset offset and count then fall through
604 pTeb
->GdiTebBatch
.Offset
-= sizeof(GDIBSTEXTOUT
);
605 pTeb
->GdiBatchCount
--;
609 return NtGdiExtTextOutW(hdc
,
629 _Out_writes_to_opt_(cwcMax
, return) LPWSTR pFaceName
)
631 /* Validate parameters */
632 if (pFaceName
&& cwcMax
<= 0)
634 /* Set last error and return failure */
635 GdiSetLastError(ERROR_INVALID_PARAMETER
);
639 /* Forward to kernel */
640 return NtGdiGetTextFaceW(hdc
, cwcMax
, pFaceName
, FALSE
);
652 _Out_writes_to_opt_(cchMax
, return) LPSTR lpName
)
657 /* Validate parameters */
658 if (lpName
&& cchMax
<= 0)
660 /* Set last error and return failure */
661 GdiSetLastError(ERROR_INVALID_PARAMETER
);
665 res
= GetTextFaceW(hdc
, 0, NULL
);
666 nameW
= HeapAlloc( GetProcessHeap(), 0, res
* 2 );
672 GetTextFaceW( hdc
, res
, nameW
);
676 if (cchMax
&& !WideCharToMultiByte( CP_ACP
, 0, nameW
, -1, lpName
, cchMax
, NULL
, NULL
))
677 lpName
[cchMax
-1] = 0;
678 res
= strlen(lpName
);
682 res
= WideCharToMultiByte( CP_ACP
, 0, nameW
, -1, NULL
, 0, NULL
, NULL
);
685 HeapFree( GetProcessHeap(), 0, nameW
);
698 _Out_writes_to_opt_(cwcMax
, return) LPWSTR pszOut
)
700 if (pszOut
&& !cwcMax
)
702 GdiSetLastError(ERROR_INVALID_PARAMETER
);
706 return NtGdiGetTextFaceW(hdc
, cwcMax
, pszOut
, TRUE
);
712 GetFontResourceInfoW(
713 _In_z_ LPCWSTR lpFileName
,
714 _Inout_ DWORD
*pdwBufSize
,
715 _Out_writes_to_opt_(*pdwBufSize
, 1) PVOID lpBuffer
,
719 UNICODE_STRING NtFileName
;
721 DPRINT("GetFontResourceInfoW: dwType = %lu\n", dwType
);
723 if (!lpFileName
|| !pdwBufSize
)
725 SetLastError(ERROR_INVALID_PARAMETER
);
729 if (!RtlDosPathNameToNtPathName_U(lpFileName
,
734 SetLastError(ERROR_PATH_NOT_FOUND
);
738 bRet
= NtGdiGetFontResourceInfoInternalW(
740 (NtFileName
.Length
/ sizeof(WCHAR
)) + 1,
747 RtlFreeHeap(RtlGetProcessHeap(), 0, NtFileName
.Buffer
);
758 SetTextCharacterExtra(
765 if (nCharExtra
== 0x80000000)
767 SetLastError(ERROR_INVALID_PARAMETER
);
771 if (GDI_HANDLE_GET_TYPE(hdc
) == GDILoObjType_LO_METADC16_TYPE
)
773 HANDLE_METADC(INT
, SetTextCharacterExtra
, 0x80000000, hdc
, nCharExtra
);
776 /* Get the DC attribute */
777 pdcattr
= GdiGetDcAttr(hdc
);
780 SetLastError(ERROR_INVALID_PARAMETER
);
784 if (NtCurrentTeb()->GdiTebBatch
.HDC
== hdc
)
786 if (pdcattr
->ulDirty_
& DC_FONTTEXT_DIRTY
)
788 NtGdiFlush(); // Sync up pdcattr from Kernel space.
789 pdcattr
->ulDirty_
&= ~(DC_MODE_DIRTY
|DC_FONTTEXT_DIRTY
);
793 nOldCharExtra
= pdcattr
->lTextExtra
;
794 pdcattr
->lTextExtra
= nCharExtra
;
795 return nOldCharExtra
;
809 /* Get the DC attribute */
810 pdcattr
= GdiGetDcAttr(hdc
);
813 /* Do not set LastError here! */
817 return pdcattr
->lTextAlign
;
832 /* Get the DC attribute */
833 pdcattr
= GdiGetDcAttr(hdc
);
836 /* Do not set LastError here! */
840 return pdcattr
->ulForegroundClr
;
856 HANDLE_METADC(BOOL
, SetTextAlign
, GDI_ERROR
, hdc
, fMode
);
858 /* Get the DC attribute */
859 pdcattr
= GdiGetDcAttr(hdc
);
862 SetLastError(ERROR_INVALID_PARAMETER
);
867 fOldMode
= pdcattr
->lTextAlign
;
868 pdcattr
->lTextAlign
= fMode
; // Raw
869 if (pdcattr
->dwLayout
& LAYOUT_RTL
)
871 if ((fMode
& TA_CENTER
) != TA_CENTER
) fMode
^= TA_RIGHT
;
874 pdcattr
->flTextAlign
= fMode
& TA_MASK
;
886 _In_ COLORREF crColor
)
891 HANDLE_METADC(COLORREF
, SetTextColor
, CLR_INVALID
, hdc
, crColor
);
893 pdcattr
= GdiGetDcAttr(hdc
);
896 SetLastError(ERROR_INVALID_PARAMETER
);
900 crOldColor
= (COLORREF
) pdcattr
->ulForegroundClr
;
901 pdcattr
->ulForegroundClr
= (ULONG
)crColor
;
903 if (pdcattr
->crForegroundClr
!= crColor
)
905 pdcattr
->ulDirty_
|= (DIRTY_TEXT
|DIRTY_LINE
|DIRTY_FILL
);
906 pdcattr
->crForegroundClr
= crColor
;
917 SetTextJustification(
919 _In_ INT nBreakExtra
,
920 _In_ INT nBreakCount
)
924 if (GDI_HANDLE_GET_TYPE(hdc
) == GDILoObjType_LO_METADC16_TYPE
)
926 HANDLE_METADC(BOOL
, SetTextJustification
, FALSE
, hdc
, nBreakExtra
, nBreakCount
);
929 /* Get the DC attribute */
930 pdcattr
= GdiGetDcAttr(hdc
);
933 /* Do not set LastError here! */
938 if (NtCurrentTeb()->GdiTebBatch
.HDC
== hdc
)
940 if (pdcattr
->ulDirty_
& DC_FONTTEXT_DIRTY
)
942 NtGdiFlush(); // Sync up pdcattr from Kernel space.
943 pdcattr
->ulDirty_
&= ~(DC_MODE_DIRTY
|DC_FONTTEXT_DIRTY
);
947 pdcattr
->cBreak
= nBreakCount
;
948 pdcattr
->lBreakExtra
= nBreakExtra
;
962 _Out_writes_(cj
) BYTE
*lpSB
)
974 Status
= HEAP_strdupA2W(&pwsz
, psz
);
975 if (!NT_SUCCESS(Status
))
977 SetLastError (RtlNtStatusToDosError(Status
));
981 uResult
= NtGdiGetStringBitmapW(hdc
, pwsz
, 1, lpSB
, cj
);
999 _Out_writes_(cj
) BYTE
*lpSB
)
1006 return NtGdiGetStringBitmapW(hdc
, pwsz
, 1, lpSB
, cj
);
1017 _Out_ EXTTEXTMETRIC
*petm
)
1021 bResult
= NtGdiGetETM(hdc
, petm
);
1023 if (bResult
&& petm
)
1025 petm
->emKernPairs
= (WORD
)GetKerningPairsA(hdc
, 0, 0);