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