xref: /reactos/win32ss/gdi/gdi32/objects/text.c (revision 49bb853b)
1 /*
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
7  */
8 
9 #include <precomp.h>
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 /*
15  * @implemented
16  */
17 BOOL
18 WINAPI
19 TextOutA(
20     _In_ HDC hdc,
21     _In_ INT nXStart,
22     _In_ INT nYStart,
23     _In_reads_(cchString) LPCSTR lpString,
24     _In_ INT cchString)
25 {
26     ANSI_STRING StringA;
27     UNICODE_STRING StringU;
28     BOOL bResult;
29     NTSTATUS Status;
30 
31     if (lpString != NULL && cchString > 0)
32     {
33         if (cchString > MAXUSHORT)
34             cchString = MAXUSHORT;
35 
36         StringA.Length = (USHORT)cchString;
37         StringA.MaximumLength = (USHORT)cchString;
38         StringA.Buffer = (PCHAR)lpString;
39 
40         Status = RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
41         if (!NT_SUCCESS(Status))
42         {
43             StringU.Buffer = NULL;
44             StringU.Length = 0;
45         }
46     }
47     else
48     {
49         StringU.Buffer = NULL;
50         StringU.Length = 0;
51     }
52 
53     bResult = TextOutW(hdc, nXStart, nYStart,
54                        StringU.Buffer, StringU.Length / sizeof(WCHAR));
55 
56     RtlFreeUnicodeString(&StringU);
57     return bResult;
58 }
59 
60 
61 /*
62  * @implemented
63  */
64 BOOL
65 WINAPI
66 TextOutW(
67     _In_ HDC hdc,
68     _In_ INT nXStart,
69     _In_ INT nYStart,
70     _In_reads_(cchString) LPCWSTR lpString,
71     _In_ INT cchString)
72 {
73     return ExtTextOutW(hdc, nXStart, nYStart, 0, NULL, (LPWSTR)lpString, cchString, NULL);
74 }
75 
76 
77 /*
78  * @unimplemented
79  */
80 BOOL
81 WINAPI
82 PolyTextOutA(
83     _In_ HDC hdc,
84     _In_reads_(cStrings) const POLYTEXTA *pptxt,
85     _In_ INT cStrings)
86 {
87     for (; cStrings>0; cStrings--, pptxt++)
88     {
89         if (!ExtTextOutA(hdc,
90                          pptxt->x,
91                          pptxt->y,
92                          pptxt->uiFlags,
93                          &pptxt->rcl,
94                          pptxt->lpstr,
95                          pptxt->n,
96                          pptxt->pdx))
97         {
98             return FALSE;
99         }
100     }
101 
102     return TRUE;
103 }
104 
105 
106 /*
107  * @unimplemented
108  */
109 BOOL
110 WINAPI
111 PolyTextOutW(
112     _In_ HDC hdc,
113     _In_reads_(cStrings) const POLYTEXTW *pptxt,
114     _In_ INT cStrings)
115 {
116     for (; cStrings>0; cStrings--, pptxt++)
117     {
118         if (!ExtTextOutW(hdc,
119                          pptxt->x,
120                          pptxt->y,
121                          pptxt->uiFlags,
122                          &pptxt->rcl,
123                          pptxt->lpstr,
124                          pptxt->n,
125                          pptxt->pdx))
126         {
127             return FALSE;
128         }
129     }
130 
131     return TRUE;
132 }
133 
134 
135 /*
136  * @implemented
137  */
138 DWORD
139 WINAPI
140 GdiGetCodePage(
141     _In_ HDC hdc)
142 {
143     PDC_ATTR pdcattr;
144 
145     /* Get the DC attribute */
146     pdcattr = GdiGetDcAttr(hdc);
147     if (pdcattr == NULL)
148     {
149         SetLastError(ERROR_INVALID_PARAMETER);
150         return 0;
151     }
152 
153     if (pdcattr->ulDirty_ & DIRTY_CHARSET)
154         return LOWORD(NtGdiGetCharSet(hdc));
155 
156     return LOWORD(pdcattr->iCS_CP);
157 }
158 
159 
160 /*
161  * @unimplemented
162  */
163 INT
164 WINAPI
165 GetTextCharacterExtra(
166     _In_ HDC hdc)
167 {
168     PDC_ATTR pdcattr;
169 
170     /* Get the DC attribute */
171     pdcattr = GdiGetDcAttr(hdc);
172     if (pdcattr == NULL)
173     {
174         /* Do not set LastError here! */
175         return 0x8000000;
176     }
177 
178     return pdcattr->lTextExtra;
179 }
180 
181 
182 /*
183  * @implemented
184  */
185 INT
186 WINAPI
187 GetTextCharset(
188     _In_ HDC hdc)
189 {
190     /* MSDN docs say this is equivalent */
191     return NtGdiGetTextCharsetInfo(hdc,NULL,0);
192 }
193 
194 
195 /*
196  * @implemented
197  */
198 BOOL
199 WINAPI
200 GetTextMetricsA(
201     _In_  HDC hdc,
202     _Out_ LPTEXTMETRICA lptm)
203 {
204     TMW_INTERNAL tmwi;
205 
206     if (!NtGdiGetTextMetricsW(hdc, &tmwi, sizeof(TMW_INTERNAL)))
207     {
208         return FALSE;
209     }
210 
211     FONT_TextMetricWToA(&tmwi.TextMetric, lptm);
212     return TRUE;
213 }
214 
215 
216 /*
217  * @implemented
218  */
219 BOOL
220 WINAPI
221 GetTextMetricsW(
222     _In_  HDC hdc,
223     _Out_ LPTEXTMETRICW lptm)
224 {
225     TMW_INTERNAL tmwi;
226 
227     if (!NtGdiGetTextMetricsW(hdc, &tmwi, sizeof(TMW_INTERNAL)))
228     {
229         return FALSE;
230     }
231 
232     *lptm = tmwi.TextMetric;
233     return TRUE;
234 }
235 
236 
237 /*
238  * @implemented
239  */
240 BOOL
241 APIENTRY
242 GetTextExtentPointA(
243     _In_ HDC hdc,
244     _In_reads_(cchString) LPCSTR lpString,
245     _In_ INT cchString,
246     _Out_ LPSIZE lpsz)
247 {
248     ANSI_STRING StringA;
249     UNICODE_STRING StringU;
250     BOOL ret;
251 
252     RtlInitAnsiString(&StringA, (LPSTR)lpString);
253     RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
254 
255     ret = GetTextExtentPointW(hdc, StringU.Buffer, cchString, lpsz);
256 
257     RtlFreeUnicodeString(&StringU);
258 
259     return ret;
260 }
261 
262 
263 /*
264  * @implemented
265  */
266 BOOL
267 APIENTRY
268 GetTextExtentPointW(
269     _In_ HDC hdc,
270     _In_reads_(cchString) LPCWSTR lpString,
271     _In_ INT cchString,
272     _Out_ LPSIZE lpsz)
273 {
274     return NtGdiGetTextExtent(hdc, (LPWSTR)lpString, cchString, lpsz, 0);
275 }
276 
277 
278 /*
279  * @implemented
280  */
281 BOOL
282 WINAPI
283 GetTextExtentExPointW(
284     _In_ HDC hdc,
285     _In_reads_(cchString) LPCWSTR lpszString,
286     _In_ INT cchString,
287     _In_ INT nMaxExtent,
288     _Out_opt_ LPINT lpnFit,
289     _Out_writes_to_opt_(cchString, *lpnFit) LPINT lpnDx,
290     _Out_ LPSIZE lpSize)
291 {
292 
293     /* Windows doesn't check nMaxExtent validity in unicode version */
294     if (nMaxExtent < -1)
295     {
296         DPRINT("nMaxExtent is invalid: %d\n", nMaxExtent);
297     }
298 
299     if (LoadLPK(LPK_GTEP))
300         return LpkGetTextExtentExPoint(hdc, lpszString, cchString, nMaxExtent, lpnFit, lpnDx, lpSize, 0, 0);
301 
302     return NtGdiGetTextExtentExW (
303                hdc, (LPWSTR)lpszString, cchString, nMaxExtent, (PULONG)lpnFit, (PULONG)lpnDx, lpSize, 0 );
304 }
305 
306 
307 /*
308  * @implemented
309  */
310 BOOL
311 WINAPI
312 GetTextExtentExPointWPri(
313     _In_ HDC hdc,
314     _In_reads_(cwc) LPCWSTR lpwsz,
315     _In_ INT cwc,
316     _In_ INT dxMax,
317     _Out_opt_ LPINT pcCh,
318     _Out_writes_to_opt_(cwc, *pcCh) LPINT pdxOut,
319     _In_ LPSIZE psize)
320 {
321     return NtGdiGetTextExtentExW(hdc, (LPWSTR)lpwsz, cwc, dxMax, (PULONG)pcCh, (PULONG)pdxOut, psize, 0);
322 }
323 
324 /*
325  * @implemented
326  */
327 BOOL
328 WINAPI
329 GetTextExtentExPointA(
330     _In_ HDC hdc,
331     _In_reads_(cchString) LPCSTR lpszStr,
332     _In_ INT cchString,
333     _In_ INT nMaxExtent,
334     _Out_opt_ LPINT lpnFit,
335     _Out_writes_to_opt_ (cchString, *lpnFit) LPINT lpnDx,
336     _Out_ LPSIZE lpSize)
337 {
338     NTSTATUS Status;
339     LPWSTR lpszStrW;
340     BOOL bResult = FALSE;
341 
342     if (nMaxExtent < -1)
343     {
344         SetLastError(ERROR_INVALID_PARAMETER);
345         return FALSE;
346     }
347 
348     Status = HEAP_strdupA2W(&lpszStrW, lpszStr);
349     if (!NT_SUCCESS (Status))
350     {
351         SetLastError(RtlNtStatusToDosError(Status));
352         return FALSE;
353     }
354 
355     bResult = NtGdiGetTextExtentExW(hdc,
356                                     lpszStrW,
357                                     cchString,
358                                     nMaxExtent,
359                                     (PULONG)lpnFit,
360                                     (PULONG)lpnDx,
361                                     lpSize,
362                                     0);
363 
364     HEAP_free(lpszStrW);
365 
366     return bResult;
367 }
368 
369 
370 /*
371  * @implemented
372  */
373 BOOL
374 WINAPI
375 GetTextExtentPoint32A(
376     _In_ HDC hdc,
377     _In_reads_(cchString) LPCSTR lpString,
378     _In_ INT cchString,
379     _Out_ LPSIZE lpSize)
380 {
381     ANSI_STRING StringA;
382     UNICODE_STRING StringU;
383     BOOL ret;
384 
385     StringA.Buffer = (LPSTR)lpString;
386     StringA.Length = cchString;
387     RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
388 
389     ret = GetTextExtentPoint32W(hdc, StringU.Buffer, cchString, lpSize);
390 
391     RtlFreeUnicodeString(&StringU);
392 
393     return ret;
394 }
395 
396 
397 /*
398  * @implemented
399  */
400 BOOL
401 WINAPI
402 GetTextExtentPoint32W(
403     _In_ HDC hdc,
404     _In_reads_(cchString) LPCWSTR lpString,
405     _In_ int cchString,
406     _Out_ LPSIZE lpSize)
407 {
408     return NtGdiGetTextExtent(hdc, (LPWSTR)lpString, cchString, lpSize, 0);
409 }
410 
411 /*
412  * @implemented
413  */
414 BOOL
415 WINAPI
416 GetTextExtentExPointI(
417     _In_ HDC hdc,
418     _In_reads_(cgi) LPWORD pgiIn,
419     _In_ INT cgi,
420     _In_ INT nMaxExtent,
421     _Out_opt_ LPINT lpnFit,
422     _Out_writes_to_opt_(cwchString, *lpnFit) LPINT lpnDx,
423     _Out_ LPSIZE lpSize)
424 {
425     return NtGdiGetTextExtentExW(hdc,
426                                  pgiIn,
427                                  cgi,
428                                  nMaxExtent,
429                                  (PULONG)lpnFit,
430                                  (PULONG)lpnDx,
431                                  lpSize,
432                                  GTEF_INDICES);
433 }
434 
435 /*
436  * @implemented
437  */
438 BOOL
439 WINAPI
440 GetTextExtentPointI(
441     _In_ HDC hdc,
442     _In_reads_(cgi) LPWORD pgiIn,
443     _In_ int cgi,
444     _Out_ LPSIZE lpSize)
445 {
446     return NtGdiGetTextExtent(hdc, pgiIn, cgi, lpSize, GTEF_INDICES);
447 }
448 
449 /*
450  * @implemented
451  */
452 BOOL
453 WINAPI
454 ExtTextOutA(
455     _In_ HDC hdc,
456     _In_ INT x,
457     _In_ INT y,
458     _In_ UINT fuOptions,
459     _In_opt_ const RECT *lprc,
460     _In_reads_opt_(cch) LPCSTR lpString,
461     _In_ UINT cch,
462     _In_reads_opt_(cch) const INT *lpDx)
463 {
464     ANSI_STRING StringA;
465     UNICODE_STRING StringU;
466     BOOL ret;
467 
468     RtlInitAnsiString(&StringA, (LPSTR)lpString);
469     RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
470 
471     ret = ExtTextOutW(hdc, x, y, fuOptions, lprc, StringU.Buffer, cch, lpDx);
472 
473     RtlFreeUnicodeString(&StringU);
474 
475     return ret;
476 }
477 
478 static BOOL bBypassETOWMF = FALSE;
479 
480 /*
481  * @implemented
482  */
483 BOOL
484 WINAPI
485 ExtTextOutW(
486     _In_ HDC hdc,
487     _In_ INT x,
488     _In_ INT y,
489     _In_ UINT fuOptions,
490     _In_opt_ const RECT *lprc,
491     _In_reads_opt_(cwc) LPCWSTR lpString,
492     _In_ UINT cwc,
493     _In_reads_opt_(cwc) const INT *lpDx)
494 {
495     PDC_ATTR pdcattr;
496 
497     // Need both, should return a parameter error? No they don't!
498     if ( !lpDx && fuOptions & ETO_PDY )
499         return FALSE;
500 
501     // Now sorting out rectangle.
502 
503     // Here again, need both.
504     if ( lprc && !(fuOptions & (ETO_CLIPPED|ETO_OPAQUE)) )
505     {
506         lprc = NULL; // No flags, no rectangle.
507     }
508     else if (!lprc) // No rectangle, force clear flags if set and continue.
509     {
510        fuOptions &= ~(ETO_CLIPPED|ETO_OPAQUE);
511     }
512 
513     if ( !bBypassETOWMF )
514     {
515         HANDLE_METADC(BOOL,
516                       ExtTextOut,
517                       FALSE,
518                       hdc,
519                       x,
520                       y,
521                       fuOptions,
522                       lprc,
523                       lpString,
524                       cwc,
525                       lpDx);
526     }
527 
528     if ( GdiConvertAndCheckDC(hdc) == NULL ) return FALSE;
529 
530     if (!(fuOptions & (ETO_GLYPH_INDEX | ETO_IGNORELANGUAGE)))
531     {
532         bBypassETOWMF = TRUE;
533 
534         if (LoadLPK(LPK_ETO))
535             return LpkExtTextOut(hdc, x, y, fuOptions, lprc, lpString, cwc , lpDx, 0);
536     }
537     else
538     {
539         bBypassETOWMF = FALSE;
540     }
541 
542     /* Get the DC attribute */
543     pdcattr = GdiGetDcAttr(hdc);
544     if ( pdcattr &&
545          !(pdcattr->ulDirty_ & DC_DIBSECTION) &&
546          !(pdcattr->lTextAlign & TA_UPDATECP))
547     {
548         if ( lprc && !cwc )
549         {
550             if ( fuOptions & ETO_OPAQUE )
551             {
552                 PGDIBSEXTTEXTOUT pgO;
553 
554                 pgO = GdiAllocBatchCommand(hdc, GdiBCExtTextOut);
555                 if (pgO)
556                 {
557                     pdcattr->ulDirty_ |= DC_MODE_DIRTY;
558                     pgO->Count = cwc;
559                     pgO->Rect = *lprc;
560                     pgO->Options = fuOptions;
561                     /* Snapshot attribute */
562                     pgO->ulBackgroundClr = pdcattr->ulBackgroundClr;
563                     pgO->ptlViewportOrg  = pdcattr->ptlViewportOrg;
564                     return TRUE;
565                 }
566             }
567             else // Do nothing, old explorer pops this off.
568             {
569                 DPRINT1("GdiBCExtTextOut nothing\n");
570                 return TRUE;
571             }
572         }         // Max 580 wchars, if offset 0
573         else if ( cwc <= ((GDIBATCHBUFSIZE - sizeof(GDIBSTEXTOUT)) / sizeof(WCHAR)) )
574         {
575             PGDIBSTEXTOUT pgO;
576             PTEB pTeb = NtCurrentTeb();
577 
578             pgO = GdiAllocBatchCommand(hdc, GdiBCTextOut);
579             if (pgO)
580             {
581                 USHORT cjSize = 0;
582                 ULONG DxSize = 0;
583 
584                 if (cwc > 2) cjSize = (cwc * sizeof(WCHAR)) - sizeof(pgO->String);
585 
586                 /* Calculate buffer size for string and Dx values */
587                 if (lpDx)
588                 {
589                     /* If ETO_PDY is specified, we have pairs of INTs */
590                     DxSize = (cwc * sizeof(INT)) * (fuOptions & ETO_PDY ? 2 : 1);
591                     cjSize += DxSize;
592                     // The structure buffer holds 4 bytes. Store Dx data then string.
593                     // Result one wchar -> Buf[ Dx ]Str[wC], [4][2][X] one extra unused wchar
594                     // to assure alignment of 4.
595                 }
596 
597                 if ((pTeb->GdiTebBatch.Offset + cjSize ) <= GDIBATCHBUFSIZE)
598                 {
599                     pdcattr->ulDirty_ |= DC_MODE_DIRTY|DC_FONTTEXT_DIRTY;
600                     pgO->cbCount = cwc;
601                     pgO->x = x;
602                     pgO->y = y;
603                     pgO->Options = fuOptions;
604                     pgO->iCS_CP = 0;
605 
606                     if (lprc) pgO->Rect = *lprc;
607                     else
608                     {
609                        pgO->Options |= GDIBS_NORECT; // Tell the other side lprc is nill.
610                     }
611 
612                     /* Snapshot attributes */
613                     pgO->crForegroundClr = pdcattr->crForegroundClr;
614                     pgO->crBackgroundClr = pdcattr->crBackgroundClr;
615                     pgO->ulForegroundClr = pdcattr->ulForegroundClr;
616                     pgO->ulBackgroundClr = pdcattr->ulBackgroundClr;
617                     pgO->lBkMode         = pdcattr->lBkMode == OPAQUE ? OPAQUE : TRANSPARENT;
618                     pgO->hlfntNew        = pdcattr->hlfntNew;
619                     pgO->flTextAlign     = pdcattr->flTextAlign;
620                     pgO->ptlViewportOrg  = pdcattr->ptlViewportOrg;
621 
622                     pgO->Size = DxSize; // of lpDx then string after.
623                     /* Put the Dx before the String to assure alignment of 4 */
624                     if (lpDx) RtlCopyMemory( &pgO->Buffer, lpDx, DxSize);
625 
626                     if (cwc) RtlCopyMemory( &pgO->String[DxSize/sizeof(WCHAR)], lpString, cwc * sizeof(WCHAR));
627 
628                     // Recompute offset and return size
629                     pTeb->GdiTebBatch.Offset += cjSize;
630                     ((PGDIBATCHHDR)pgO)->Size += cjSize;
631                     return TRUE;
632                 }
633                 // Reset offset and count then fall through
634                 pTeb->GdiTebBatch.Offset -= sizeof(GDIBSTEXTOUT);
635                 pTeb->GdiBatchCount--;
636             }
637         }
638     }
639     return NtGdiExtTextOutW(hdc,
640                             x,
641                             y,
642                             fuOptions,
643                             (LPRECT)lprc,
644                             (LPWSTR)lpString,
645                             cwc,
646                             (LPINT)lpDx,
647                             0);
648 }
649 
650 
651 /*
652  * @implemented
653  */
654 INT
655 WINAPI
656 GetTextFaceW(
657     _In_ HDC hdc,
658     _In_ INT cwcMax,
659     _Out_writes_to_opt_(cwcMax, return) LPWSTR pFaceName)
660 {
661     /* Validate parameters */
662     if (pFaceName && cwcMax <= 0)
663     {
664         /* Set last error and return failure */
665         GdiSetLastError(ERROR_INVALID_PARAMETER);
666         return 0;
667     }
668 
669     /* Forward to kernel */
670     return NtGdiGetTextFaceW(hdc, cwcMax, pFaceName, FALSE);
671 }
672 
673 
674 /*
675  * @implemented
676  */
677 INT
678 WINAPI
679 GetTextFaceA(
680     _In_ HDC hdc,
681     _In_ INT cchMax,
682     _Out_writes_to_opt_(cchMax, return) LPSTR lpName)
683 {
684     INT res;
685     LPWSTR nameW;
686 
687     /* Validate parameters */
688     if (lpName && cchMax <= 0)
689     {
690         /* Set last error and return failure */
691         GdiSetLastError(ERROR_INVALID_PARAMETER);
692         return 0;
693     }
694 
695     res = GetTextFaceW(hdc, 0, NULL);
696     nameW = HeapAlloc( GetProcessHeap(), 0, res * 2 );
697     if (nameW == NULL)
698     {
699         return 0;
700     }
701 
702     GetTextFaceW( hdc, res, nameW );
703 
704     if (lpName)
705     {
706         if (cchMax && !WideCharToMultiByte( CP_ACP, 0, nameW, -1, lpName, cchMax, NULL, NULL))
707             lpName[cchMax-1] = 0;
708         res = strlen(lpName);
709     }
710     else
711     {
712         res = WideCharToMultiByte( CP_ACP, 0, nameW, -1, NULL, 0, NULL, NULL);
713     }
714 
715     HeapFree( GetProcessHeap(), 0, nameW );
716     return res;
717 }
718 
719 
720 /*
721  * @implemented
722  */
723 INT
724 WINAPI
725 GetTextFaceAliasW(
726     _In_ HDC hdc,
727     _In_ INT cwcMax,
728     _Out_writes_to_opt_(cwcMax, return) LPWSTR pszOut)
729 {
730     if (pszOut && !cwcMax)
731     {
732         GdiSetLastError(ERROR_INVALID_PARAMETER);
733         return 0;
734     }
735 
736     return NtGdiGetTextFaceW(hdc, cwcMax, pszOut, TRUE);
737 }
738 
739 
740 BOOL
741 WINAPI
742 GetFontResourceInfoW(
743     _In_z_ LPCWSTR lpFileName,
744     _Inout_ DWORD *pdwBufSize,
745     _Out_writes_to_opt_(*pdwBufSize, 1) PVOID lpBuffer,
746     _In_ DWORD dwType)
747 {
748     BOOL bRet;
749     UNICODE_STRING NtFileName;
750 
751     DPRINT("GetFontResourceInfoW: dwType = %lu\n", dwType);
752 
753     if (!lpFileName || !pdwBufSize)
754     {
755         SetLastError(ERROR_INVALID_PARAMETER);
756         return FALSE;
757     }
758 
759     if (!RtlDosPathNameToNtPathName_U(lpFileName,
760                                       &NtFileName,
761                                       NULL,
762                                       NULL))
763     {
764         SetLastError(ERROR_PATH_NOT_FOUND);
765         return FALSE;
766     }
767 
768     bRet = NtGdiGetFontResourceInfoInternalW(
769                NtFileName.Buffer,
770                (NtFileName.Length / sizeof(WCHAR)) + 1,
771                1,
772                *pdwBufSize,
773                pdwBufSize,
774                lpBuffer,
775                dwType);
776 
777     RtlFreeHeap(RtlGetProcessHeap(), 0, NtFileName.Buffer);
778 
779     return bRet;
780 }
781 
782 
783 /*
784  * @unimplemented
785  */
786 INT
787 WINAPI
788 SetTextCharacterExtra(
789     _In_ HDC hdc,
790     _In_ INT nCharExtra)
791 {
792     PDC_ATTR pdcattr;
793     INT nOldCharExtra;
794 
795     if (nCharExtra == 0x80000000)
796     {
797         SetLastError(ERROR_INVALID_PARAMETER);
798         return 0x80000000;
799     }
800 
801     HANDLE_METADC16(INT, SetTextCharacterExtra, 0x80000000, hdc, nCharExtra);
802 
803     /* Get the DC attribute */
804     pdcattr = GdiGetDcAttr(hdc);
805     if (pdcattr == NULL)
806     {
807         SetLastError(ERROR_INVALID_PARAMETER);
808         return 0x8000000;
809     }
810 
811     if (NtCurrentTeb()->GdiTebBatch.HDC == hdc)
812     {
813         if (pdcattr->ulDirty_ & DC_FONTTEXT_DIRTY)
814         {
815             NtGdiFlush(); // Sync up pdcattr from Kernel space.
816             pdcattr->ulDirty_ &= ~(DC_MODE_DIRTY|DC_FONTTEXT_DIRTY);
817         }
818     }
819 
820     nOldCharExtra = pdcattr->lTextExtra;
821     pdcattr->lTextExtra = nCharExtra;
822     return nOldCharExtra;
823 }
824 
825 /*
826  * @implemented
827  *
828  */
829 UINT
830 WINAPI
831 GetTextAlign(
832     _In_ HDC hdc)
833 {
834     PDC_ATTR pdcattr;
835 
836     /* Get the DC attribute */
837     pdcattr = GdiGetDcAttr(hdc);
838     if (pdcattr == NULL)
839     {
840         /* Do not set LastError here! */
841         return GDI_ERROR;
842     }
843 
844     return pdcattr->lTextAlign;
845 }
846 
847 
848 /*
849  * @implemented
850  *
851  */
852 COLORREF
853 WINAPI
854 GetTextColor(
855     _In_ HDC hdc)
856 {
857     PDC_ATTR pdcattr;
858 
859     /* Get the DC attribute */
860     pdcattr = GdiGetDcAttr(hdc);
861     if (pdcattr == NULL)
862     {
863         /* Do not set LastError here! */
864         return CLR_INVALID;
865     }
866 
867     return pdcattr->ulForegroundClr;
868 }
869 
870 
871 /*
872  * @unimplemented
873  */
874 UINT
875 WINAPI
876 SetTextAlign(
877     _In_ HDC hdc,
878     _In_ UINT fMode)
879 {
880     PDC_ATTR pdcattr;
881     UINT fOldMode;
882 
883     HANDLE_METADC(BOOL, SetTextAlign, GDI_ERROR, hdc, fMode);
884 
885     /* Get the DC attribute */
886     pdcattr = GdiGetDcAttr(hdc);
887     if (pdcattr == NULL)
888     {
889         SetLastError(ERROR_INVALID_PARAMETER);
890         return GDI_ERROR;
891     }
892 
893 
894     fOldMode = pdcattr->lTextAlign;
895     pdcattr->lTextAlign = fMode; // Raw
896     if (pdcattr->dwLayout & LAYOUT_RTL)
897     {
898         if ((fMode & TA_CENTER) != TA_CENTER) fMode ^= TA_RIGHT;
899     }
900 
901     pdcattr->flTextAlign = fMode & TA_MASK;
902     return fOldMode;
903 }
904 
905 
906 /*
907  * @implemented
908  */
909 COLORREF
910 WINAPI
911 SetTextColor(
912     _In_ HDC hdc,
913     _In_ COLORREF crColor)
914 {
915     PDC_ATTR pdcattr;
916     COLORREF crOldColor;
917 
918     HANDLE_METADC(COLORREF, SetTextColor, CLR_INVALID, hdc, crColor);
919 
920     pdcattr = GdiGetDcAttr(hdc);
921     if (pdcattr == NULL)
922     {
923         SetLastError(ERROR_INVALID_PARAMETER);
924         return CLR_INVALID;
925     }
926 
927     crOldColor = (COLORREF) pdcattr->ulForegroundClr;
928     pdcattr->ulForegroundClr = (ULONG)crColor;
929 
930     if (pdcattr->crForegroundClr != crColor)
931     {
932         pdcattr->ulDirty_ |= (DIRTY_TEXT|DIRTY_LINE|DIRTY_FILL);
933         pdcattr->crForegroundClr = crColor;
934     }
935 
936     return crOldColor;
937 }
938 
939 /*
940  * @implemented
941  */
942 BOOL
943 WINAPI
944 SetTextJustification(
945     _In_ HDC hdc,
946     _In_ INT nBreakExtra,
947     _In_ INT nBreakCount)
948 {
949     PDC_ATTR pdcattr;
950 
951     HANDLE_METADC16(BOOL, SetTextJustification, FALSE, hdc, nBreakExtra, nBreakCount);
952 
953     /* Get the DC attribute */
954     pdcattr = GdiGetDcAttr(hdc);
955     if (pdcattr == NULL)
956     {
957         /* Do not set LastError here! */
958         return GDI_ERROR;
959     }
960 
961 
962     if (NtCurrentTeb()->GdiTebBatch.HDC == hdc)
963     {
964         if (pdcattr->ulDirty_ & DC_FONTTEXT_DIRTY)
965         {
966             NtGdiFlush(); // Sync up pdcattr from Kernel space.
967             pdcattr->ulDirty_ &= ~(DC_MODE_DIRTY|DC_FONTTEXT_DIRTY);
968         }
969     }
970 
971     pdcattr->cBreak = nBreakCount;
972     pdcattr->lBreakExtra = nBreakExtra;
973     return TRUE;
974 }
975 
976 /*
977  * @implemented
978  */
979 UINT
980 WINAPI
981 GetStringBitmapA(
982     _In_ HDC hdc,
983     _In_ LPSTR psz,
984     _In_ BOOL bDoCall,
985     _In_ UINT cj,
986     _Out_writes_(cj) BYTE *lpSB)
987 {
988 
989     NTSTATUS Status;
990     PWSTR pwsz;
991     UINT uResult = 0;
992 
993     if (!bDoCall)
994     {
995         return 0;
996     }
997 
998     Status = HEAP_strdupA2W(&pwsz, psz);
999     if (!NT_SUCCESS(Status))
1000     {
1001         SetLastError (RtlNtStatusToDosError(Status));
1002     }
1003     else
1004     {
1005         uResult = NtGdiGetStringBitmapW(hdc, pwsz, 1, lpSB, cj);
1006         HEAP_free(pwsz);
1007     }
1008 
1009     return uResult;
1010 
1011 }
1012 
1013 /*
1014  * @implemented
1015  */
1016 UINT
1017 WINAPI
1018 GetStringBitmapW(
1019     _In_ HDC hdc,
1020     _In_ LPWSTR pwsz,
1021     _In_ BOOL bDoCall,
1022     _In_ UINT cj,
1023     _Out_writes_(cj) BYTE *lpSB)
1024 {
1025     if (!bDoCall)
1026     {
1027         return 0;
1028     }
1029 
1030     return NtGdiGetStringBitmapW(hdc, pwsz, 1, lpSB, cj);
1031 
1032 }
1033 
1034 /*
1035  * @implemented
1036  */
1037 BOOL
1038 WINAPI
1039 GetETM(
1040     _In_ HDC hdc,
1041     _Out_ EXTTEXTMETRIC *petm)
1042 {
1043     BOOL bResult;
1044 
1045     bResult = NtGdiGetETM(hdc, petm);
1046 
1047     if (bResult && petm)
1048     {
1049         petm->emKernPairs = (WORD)GetKerningPairsA(hdc, 0, 0);
1050     }
1051 
1052     return bResult;
1053 }
1054