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