xref: /reactos/win32ss/gdi/gdi32/objects/text.c (revision 8a978a17)
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 
479 /*
480  * @implemented
481  */
482 BOOL
483 WINAPI
484 ExtTextOutW(
485     _In_ HDC hdc,
486     _In_ INT x,
487     _In_ INT y,
488     _In_ UINT fuOptions,
489     _In_opt_ const RECT *lprc,
490     _In_reads_opt_(cwc) LPCWSTR lpString,
491     _In_ UINT cwc,
492     _In_reads_opt_(cwc) const INT *lpDx)
493 {
494     PDC_ATTR pdcattr;
495 
496     HANDLE_METADC(BOOL,
497                   ExtTextOut,
498                   FALSE,
499                   hdc,
500                   x,
501                   y,
502                   fuOptions,
503                   lprc,
504                   lpString,
505                   cwc,
506                   lpDx);
507 
508     if ( GdiConvertAndCheckDC(hdc) == NULL ) return FALSE;
509 
510     if (!(fuOptions & (ETO_GLYPH_INDEX | ETO_IGNORELANGUAGE)))
511     {
512         if (LoadLPK(LPK_ETO))
513             return LpkExtTextOut(hdc, x, y, fuOptions, lprc, lpString, cwc , lpDx, 0);
514     }
515 
516     /* Get the DC attribute */
517     pdcattr = GdiGetDcAttr(hdc);
518     if ( pdcattr &&
519          !(pdcattr->ulDirty_ & DC_DIBSECTION) &&
520          !(pdcattr->lTextAlign & TA_UPDATECP))
521     {
522         if ( lprc && !cwc )
523         {
524             if ( fuOptions & ETO_OPAQUE )
525             {
526                 PGDIBSEXTTEXTOUT pgO;
527 
528                 pgO = GdiAllocBatchCommand(hdc, GdiBCExtTextOut);
529                 if (pgO)
530                 {
531                     pdcattr->ulDirty_ |= DC_MODE_DIRTY;
532                     pgO->Count = cwc;
533                     pgO->Rect = *lprc;
534                     pgO->Options = fuOptions;
535                     /* Snapshot attribute */
536                     pgO->ulBackgroundClr = pdcattr->ulBackgroundClr;
537                     pgO->ptlViewportOrg  = pdcattr->ptlViewportOrg;
538                     return TRUE;
539                 }
540             }
541             else // Do nothing, old explorer pops this off.
542             {
543                 DPRINT1("GdiBCExtTextOut nothing\n");
544                 return TRUE;
545             }
546         }         // Max 580 wchars, if offset 0
547         else if ( cwc <= ((GDIBATCHBUFSIZE - sizeof(GDIBSTEXTOUT)) / sizeof(WCHAR)) )
548         {
549             PGDIBSTEXTOUT pgO;
550             PTEB pTeb = NtCurrentTeb();
551 
552             pgO = GdiAllocBatchCommand(hdc, GdiBCTextOut);
553             if (pgO)
554             {
555                 USHORT cjSize = 0;
556                 ULONG DxSize = 0;
557 
558                 if (cwc > 2) cjSize = (cwc * sizeof(WCHAR)) - sizeof(pgO->String);
559 
560                 /* Calculate buffer size for string and Dx values */
561                 if (lpDx)
562                 {
563                     /* If ETO_PDY is specified, we have pairs of INTs */
564                     DxSize = (cwc * sizeof(INT)) * (fuOptions & ETO_PDY ? 2 : 1);
565                     cjSize += DxSize;
566                     // The structure buffer holds 4 bytes. Store Dx data then string.
567                     // Result one wchar -> Buf[ Dx ]Str[wC], [4][2][X] one extra unused wchar
568                     // to assure alignment of 4.
569                 }
570 
571                 if ((pTeb->GdiTebBatch.Offset + cjSize ) <= GDIBATCHBUFSIZE)
572                 {
573                     pdcattr->ulDirty_ |= DC_MODE_DIRTY|DC_FONTTEXT_DIRTY;
574                     pgO->cbCount = cwc;
575                     pgO->x = x;
576                     pgO->y = y;
577                     pgO->Options = fuOptions;
578                     pgO->iCS_CP = 0;
579 
580                     if (lprc) pgO->Rect = *lprc;
581                     else
582                     {
583                        pgO->Options |= GDIBS_NORECT; // Tell the other side lprc is nill.
584                     }
585 
586                     /* Snapshot attributes */
587                     pgO->crForegroundClr = pdcattr->crForegroundClr;
588                     pgO->crBackgroundClr = pdcattr->crBackgroundClr;
589                     pgO->ulForegroundClr = pdcattr->ulForegroundClr;
590                     pgO->ulBackgroundClr = pdcattr->ulBackgroundClr;
591                     pgO->lBkMode         = pdcattr->lBkMode == OPAQUE ? OPAQUE : TRANSPARENT;
592                     pgO->hlfntNew        = pdcattr->hlfntNew;
593                     pgO->flTextAlign     = pdcattr->flTextAlign;
594                     pgO->ptlViewportOrg  = pdcattr->ptlViewportOrg;
595 
596                     pgO->Size = DxSize; // of lpDx then string after.
597                     /* Put the Dx before the String to assure alignment of 4 */
598                     if (lpDx) RtlCopyMemory( &pgO->Buffer, lpDx, DxSize);
599 
600                     if (cwc) RtlCopyMemory( &pgO->String[DxSize/sizeof(WCHAR)], lpString, cwc * sizeof(WCHAR));
601 
602                     // Recompute offset and return size
603                     pTeb->GdiTebBatch.Offset += cjSize;
604                     ((PGDIBATCHHDR)pgO)->Size += cjSize;
605                     return TRUE;
606                 }
607                 // Reset offset and count then fall through
608                 pTeb->GdiTebBatch.Offset -= sizeof(GDIBSTEXTOUT);
609                 pTeb->GdiBatchCount--;
610             }
611         }
612     }
613     return NtGdiExtTextOutW(hdc,
614                             x,
615                             y,
616                             fuOptions,
617                             (LPRECT)lprc,
618                             (LPWSTR)lpString,
619                             cwc,
620                             (LPINT)lpDx,
621                             0);
622 }
623 
624 
625 /*
626  * @implemented
627  */
628 INT
629 WINAPI
630 GetTextFaceW(
631     _In_ HDC hdc,
632     _In_ INT cwcMax,
633     _Out_writes_to_opt_(cwcMax, return) LPWSTR pFaceName)
634 {
635     /* Validate parameters */
636     if (pFaceName && cwcMax <= 0)
637     {
638         /* Set last error and return failure */
639         GdiSetLastError(ERROR_INVALID_PARAMETER);
640         return 0;
641     }
642 
643     /* Forward to kernel */
644     return NtGdiGetTextFaceW(hdc, cwcMax, pFaceName, FALSE);
645 }
646 
647 
648 /*
649  * @implemented
650  */
651 INT
652 WINAPI
653 GetTextFaceA(
654     _In_ HDC hdc,
655     _In_ INT cchMax,
656     _Out_writes_to_opt_(cchMax, return) LPSTR lpName)
657 {
658     INT res;
659     LPWSTR nameW;
660 
661     /* Validate parameters */
662     if (lpName && cchMax <= 0)
663     {
664         /* Set last error and return failure */
665         GdiSetLastError(ERROR_INVALID_PARAMETER);
666         return 0;
667     }
668 
669     res = GetTextFaceW(hdc, 0, NULL);
670     nameW = HeapAlloc( GetProcessHeap(), 0, res * 2 );
671     if (nameW == NULL)
672     {
673         return 0;
674     }
675 
676     GetTextFaceW( hdc, res, nameW );
677 
678     if (lpName)
679     {
680         if (cchMax && !WideCharToMultiByte( CP_ACP, 0, nameW, -1, lpName, cchMax, NULL, NULL))
681             lpName[cchMax-1] = 0;
682         res = strlen(lpName);
683     }
684     else
685     {
686         res = WideCharToMultiByte( CP_ACP, 0, nameW, -1, NULL, 0, NULL, NULL);
687     }
688 
689     HeapFree( GetProcessHeap(), 0, nameW );
690     return res;
691 }
692 
693 
694 /*
695  * @implemented
696  */
697 INT
698 WINAPI
699 GetTextFaceAliasW(
700     _In_ HDC hdc,
701     _In_ INT cwcMax,
702     _Out_writes_to_opt_(cwcMax, return) LPWSTR pszOut)
703 {
704     if (pszOut && !cwcMax)
705     {
706         GdiSetLastError(ERROR_INVALID_PARAMETER);
707         return 0;
708     }
709 
710     return NtGdiGetTextFaceW(hdc, cwcMax, pszOut, TRUE);
711 }
712 
713 
714 BOOL
715 WINAPI
716 GetFontResourceInfoW(
717     _In_z_ LPCWSTR lpFileName,
718     _Inout_ DWORD *pdwBufSize,
719     _Out_writes_to_opt_(*pdwBufSize, 1) PVOID lpBuffer,
720     _In_ DWORD dwType)
721 {
722     BOOL bRet;
723     UNICODE_STRING NtFileName;
724 
725     DPRINT("GetFontResourceInfoW: dwType = %lu\n", dwType);
726 
727     if (!lpFileName || !pdwBufSize)
728     {
729         SetLastError(ERROR_INVALID_PARAMETER);
730         return FALSE;
731     }
732 
733     if (!RtlDosPathNameToNtPathName_U(lpFileName,
734                                       &NtFileName,
735                                       NULL,
736                                       NULL))
737     {
738         SetLastError(ERROR_PATH_NOT_FOUND);
739         return FALSE;
740     }
741 
742     bRet = NtGdiGetFontResourceInfoInternalW(
743                NtFileName.Buffer,
744                (NtFileName.Length / sizeof(WCHAR)) + 1,
745                1,
746                *pdwBufSize,
747                pdwBufSize,
748                lpBuffer,
749                dwType);
750 
751     RtlFreeHeap(RtlGetProcessHeap(), 0, NtFileName.Buffer);
752 
753     return bRet;
754 }
755 
756 
757 /*
758  * @unimplemented
759  */
760 INT
761 WINAPI
762 SetTextCharacterExtra(
763     _In_ HDC hdc,
764     _In_ INT nCharExtra)
765 {
766     PDC_ATTR pdcattr;
767     INT nOldCharExtra;
768 
769     if (nCharExtra == 0x80000000)
770     {
771         SetLastError(ERROR_INVALID_PARAMETER);
772         return 0x80000000;
773     }
774 
775     if (GDI_HANDLE_GET_TYPE(hdc) == GDILoObjType_LO_METADC16_TYPE)
776     {
777         HANDLE_METADC(INT, SetTextCharacterExtra, 0x80000000, hdc, nCharExtra);
778     }
779 
780     /* Get the DC attribute */
781     pdcattr = GdiGetDcAttr(hdc);
782     if (pdcattr == NULL)
783     {
784         SetLastError(ERROR_INVALID_PARAMETER);
785         return 0x8000000;
786     }
787 
788     if (NtCurrentTeb()->GdiTebBatch.HDC == hdc)
789     {
790         if (pdcattr->ulDirty_ & DC_FONTTEXT_DIRTY)
791         {
792             NtGdiFlush(); // Sync up pdcattr from Kernel space.
793             pdcattr->ulDirty_ &= ~(DC_MODE_DIRTY|DC_FONTTEXT_DIRTY);
794         }
795     }
796 
797     nOldCharExtra = pdcattr->lTextExtra;
798     pdcattr->lTextExtra = nCharExtra;
799     return nOldCharExtra;
800 }
801 
802 /*
803  * @implemented
804  *
805  */
806 UINT
807 WINAPI
808 GetTextAlign(
809     _In_ HDC hdc)
810 {
811     PDC_ATTR pdcattr;
812 
813     /* Get the DC attribute */
814     pdcattr = GdiGetDcAttr(hdc);
815     if (pdcattr == NULL)
816     {
817         /* Do not set LastError here! */
818         return GDI_ERROR;
819     }
820 
821     return pdcattr->lTextAlign;
822 }
823 
824 
825 /*
826  * @implemented
827  *
828  */
829 COLORREF
830 WINAPI
831 GetTextColor(
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 CLR_INVALID;
842     }
843 
844     return pdcattr->ulForegroundClr;
845 }
846 
847 
848 /*
849  * @unimplemented
850  */
851 UINT
852 WINAPI
853 SetTextAlign(
854     _In_ HDC hdc,
855     _In_ UINT fMode)
856 {
857     PDC_ATTR pdcattr;
858     UINT fOldMode;
859 
860     HANDLE_METADC(BOOL, SetTextAlign, GDI_ERROR, hdc, fMode);
861 
862     /* Get the DC attribute */
863     pdcattr = GdiGetDcAttr(hdc);
864     if (pdcattr == NULL)
865     {
866         SetLastError(ERROR_INVALID_PARAMETER);
867         return GDI_ERROR;
868     }
869 
870 
871     fOldMode = pdcattr->lTextAlign;
872     pdcattr->lTextAlign = fMode; // Raw
873     if (pdcattr->dwLayout & LAYOUT_RTL)
874     {
875         if ((fMode & TA_CENTER) != TA_CENTER) fMode ^= TA_RIGHT;
876     }
877 
878     pdcattr->flTextAlign = fMode & TA_MASK;
879     return fOldMode;
880 }
881 
882 
883 /*
884  * @implemented
885  */
886 COLORREF
887 WINAPI
888 SetTextColor(
889     _In_ HDC hdc,
890     _In_ COLORREF crColor)
891 {
892     PDC_ATTR pdcattr;
893     COLORREF crOldColor;
894 
895     HANDLE_METADC(COLORREF, SetTextColor, CLR_INVALID, hdc, crColor);
896 
897     pdcattr = GdiGetDcAttr(hdc);
898     if (pdcattr == NULL)
899     {
900         SetLastError(ERROR_INVALID_PARAMETER);
901         return CLR_INVALID;
902     }
903 
904     crOldColor = (COLORREF) pdcattr->ulForegroundClr;
905     pdcattr->ulForegroundClr = (ULONG)crColor;
906 
907     if (pdcattr->crForegroundClr != crColor)
908     {
909         pdcattr->ulDirty_ |= (DIRTY_TEXT|DIRTY_LINE|DIRTY_FILL);
910         pdcattr->crForegroundClr = crColor;
911     }
912 
913     return crOldColor;
914 }
915 
916 /*
917  * @implemented
918  */
919 BOOL
920 WINAPI
921 SetTextJustification(
922     _In_ HDC hdc,
923     _In_ INT nBreakExtra,
924     _In_ INT nBreakCount)
925 {
926     PDC_ATTR pdcattr;
927 
928     if (GDI_HANDLE_GET_TYPE(hdc) == GDILoObjType_LO_METADC16_TYPE)
929     {
930         HANDLE_METADC(BOOL, SetTextJustification, FALSE, hdc, nBreakExtra, nBreakCount);
931     }
932 
933     /* Get the DC attribute */
934     pdcattr = GdiGetDcAttr(hdc);
935     if (pdcattr == NULL)
936     {
937         /* Do not set LastError here! */
938         return GDI_ERROR;
939     }
940 
941 
942     if (NtCurrentTeb()->GdiTebBatch.HDC == hdc)
943     {
944         if (pdcattr->ulDirty_ & DC_FONTTEXT_DIRTY)
945         {
946             NtGdiFlush(); // Sync up pdcattr from Kernel space.
947             pdcattr->ulDirty_ &= ~(DC_MODE_DIRTY|DC_FONTTEXT_DIRTY);
948         }
949     }
950 
951     pdcattr->cBreak = nBreakCount;
952     pdcattr->lBreakExtra = nBreakExtra;
953     return TRUE;
954 }
955 
956 /*
957  * @implemented
958  */
959 UINT
960 WINAPI
961 GetStringBitmapA(
962     _In_ HDC hdc,
963     _In_ LPSTR psz,
964     _In_ BOOL bDoCall,
965     _In_ UINT cj,
966     _Out_writes_(cj) BYTE *lpSB)
967 {
968 
969     NTSTATUS Status;
970     PWSTR pwsz;
971     UINT uResult = 0;
972 
973     if (!bDoCall)
974     {
975         return 0;
976     }
977 
978     Status = HEAP_strdupA2W(&pwsz, psz);
979     if (!NT_SUCCESS(Status))
980     {
981         SetLastError (RtlNtStatusToDosError(Status));
982     }
983     else
984     {
985         uResult = NtGdiGetStringBitmapW(hdc, pwsz, 1, lpSB, cj);
986         HEAP_free(pwsz);
987     }
988 
989     return uResult;
990 
991 }
992 
993 /*
994  * @implemented
995  */
996 UINT
997 WINAPI
998 GetStringBitmapW(
999     _In_ HDC hdc,
1000     _In_ LPWSTR pwsz,
1001     _In_ BOOL bDoCall,
1002     _In_ UINT cj,
1003     _Out_writes_(cj) BYTE *lpSB)
1004 {
1005     if (!bDoCall)
1006     {
1007         return 0;
1008     }
1009 
1010     return NtGdiGetStringBitmapW(hdc, pwsz, 1, lpSB, cj);
1011 
1012 }
1013 
1014 /*
1015  * @implemented
1016  */
1017 BOOL
1018 WINAPI
1019 GetETM(
1020     _In_ HDC hdc,
1021     _Out_ EXTTEXTMETRIC *petm)
1022 {
1023     BOOL bResult;
1024 
1025     bResult = NtGdiGetETM(hdc, petm);
1026 
1027     if (bResult && petm)
1028     {
1029         petm->emKernPairs = (WORD)GetKerningPairsA(hdc, 0, 0);
1030     }
1031 
1032     return bResult;
1033 }
1034