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