xref: /reactos/win32ss/gdi/gdi32/objects/text.c (revision 2b91b296)
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     return NtGdiGetTextExtentExW (
300                hdc, (LPWSTR)lpszString, cchString, nMaxExtent, (PULONG)lpnFit, (PULONG)lpnDx, lpSize, 0 );
301 }
302 
303 
304 /*
305  * @implemented
306  */
307 BOOL
308 WINAPI
309 GetTextExtentExPointWPri(
310     _In_ HDC hdc,
311     _In_reads_(cwc) LPWSTR lpwsz,
312     _In_ ULONG cwc,
313     _In_ ULONG dxMax,
314     _Out_opt_ ULONG *pcCh,
315     _Out_writes_to_opt_(cwc, *pcCh) PULONG pdxOut,
316     _In_ LPSIZE psize)
317 {
318     return NtGdiGetTextExtentExW(hdc, lpwsz, cwc, dxMax, pcCh, pdxOut, psize, 0);
319 }
320 
321 /*
322  * @implemented
323  */
324 BOOL
325 WINAPI
326 GetTextExtentExPointA(
327     _In_ HDC hdc,
328     _In_reads_(cchString) LPCSTR lpszStr,
329     _In_ INT cchString,
330     _In_ INT nMaxExtent,
331     _Out_opt_ LPINT lpnFit,
332     _Out_writes_to_opt_ (cchString, *lpnFit) LPINT lpnDx,
333     _Out_ LPSIZE lpSize)
334 {
335     NTSTATUS Status;
336     LPWSTR lpszStrW;
337     BOOL bResult = FALSE;
338 
339     if (nMaxExtent < -1)
340     {
341         SetLastError(ERROR_INVALID_PARAMETER);
342         return FALSE;
343     }
344 
345     Status = HEAP_strdupA2W(&lpszStrW, lpszStr);
346     if (!NT_SUCCESS (Status))
347     {
348         SetLastError(RtlNtStatusToDosError(Status));
349         return FALSE;
350     }
351 
352     bResult = NtGdiGetTextExtentExW(hdc,
353                                     lpszStrW,
354                                     cchString,
355                                     nMaxExtent,
356                                     (PULONG)lpnFit,
357                                     (PULONG)lpnDx,
358                                     lpSize,
359                                     0);
360 
361     HEAP_free(lpszStrW);
362 
363     return bResult;
364 }
365 
366 
367 /*
368  * @implemented
369  */
370 BOOL
371 WINAPI
372 GetTextExtentPoint32A(
373     _In_ HDC hdc,
374     _In_reads_(cchString) LPCSTR lpString,
375     _In_ INT cchString,
376     _Out_ LPSIZE lpSize)
377 {
378     ANSI_STRING StringA;
379     UNICODE_STRING StringU;
380     BOOL ret;
381 
382     StringA.Buffer = (LPSTR)lpString;
383     StringA.Length = cchString;
384     RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
385 
386     ret = GetTextExtentPoint32W(hdc, StringU.Buffer, cchString, lpSize);
387 
388     RtlFreeUnicodeString(&StringU);
389 
390     return ret;
391 }
392 
393 
394 /*
395  * @implemented
396  */
397 BOOL
398 WINAPI
399 GetTextExtentPoint32W(
400     _In_ HDC hdc,
401     _In_reads_(cchString) LPCWSTR lpString,
402     _In_ int cchString,
403     _Out_ LPSIZE lpSize)
404 {
405     return NtGdiGetTextExtent(hdc, (LPWSTR)lpString, cchString, lpSize, 0);
406 }
407 
408 /*
409  * @implemented
410  */
411 BOOL
412 WINAPI
413 GetTextExtentExPointI(
414     _In_ HDC hdc,
415     _In_reads_(cgi) LPWORD pgiIn,
416     _In_ INT cgi,
417     _In_ INT nMaxExtent,
418     _Out_opt_ LPINT lpnFit,
419     _Out_writes_to_opt_(cwchString, *lpnFit) LPINT lpnDx,
420     _Out_ LPSIZE lpSize)
421 {
422     return NtGdiGetTextExtentExW(hdc,
423                                  pgiIn,
424                                  cgi,
425                                  nMaxExtent,
426                                  (PULONG)lpnFit,
427                                  (PULONG)lpnDx,
428                                  lpSize,
429                                  GTEF_INDICES);
430 }
431 
432 /*
433  * @implemented
434  */
435 BOOL
436 WINAPI
437 GetTextExtentPointI(
438     _In_ HDC hdc,
439     _In_reads_(cgi) LPWORD pgiIn,
440     _In_ int cgi,
441     _Out_ LPSIZE lpSize)
442 {
443     return NtGdiGetTextExtent(hdc, pgiIn, cgi, lpSize, GTEF_INDICES);
444 }
445 
446 /*
447  * @implemented
448  */
449 BOOL
450 WINAPI
451 ExtTextOutA(
452     _In_ HDC hdc,
453     _In_ INT x,
454     _In_ INT y,
455     _In_ UINT fuOptions,
456     _In_opt_ const RECT *lprc,
457     _In_reads_opt_(cch) LPCSTR lpString,
458     _In_ UINT cch,
459     _In_reads_opt_(cch) const INT *lpDx)
460 {
461     ANSI_STRING StringA;
462     UNICODE_STRING StringU;
463     BOOL ret;
464 
465     RtlInitAnsiString(&StringA, (LPSTR)lpString);
466     RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
467 
468     ret = ExtTextOutW(hdc, x, y, fuOptions, lprc, StringU.Buffer, cch, lpDx);
469 
470     RtlFreeUnicodeString(&StringU);
471 
472     return ret;
473 }
474 
475 
476 /*
477  * @implemented
478  */
479 BOOL
480 WINAPI
481 ExtTextOutW(
482     _In_ HDC hdc,
483     _In_ INT x,
484     _In_ INT y,
485     _In_ UINT fuOptions,
486     _In_opt_ const RECT *lprc,
487     _In_reads_opt_(cwc) LPCWSTR lpString,
488     _In_ UINT cwc,
489     _In_reads_opt_(cwc) const INT *lpDx)
490 {
491     HANDLE_METADC(BOOL,
492                   ExtTextOut,
493                   FALSE,
494                   hdc,
495                   x,
496                   y,
497                   fuOptions,
498                   lprc,
499                   lpString,
500                   cwc,
501                   lpDx);
502 
503     return NtGdiExtTextOutW(hdc,
504                             x,
505                             y,
506                             fuOptions,
507                             (LPRECT)lprc,
508                             (LPWSTR)lpString,
509                             cwc,
510                             (LPINT)lpDx,
511                             0);
512 }
513 
514 
515 /*
516  * @implemented
517  */
518 INT
519 WINAPI
520 GetTextFaceW(
521     _In_ HDC hdc,
522     _In_ INT cwcMax,
523     _Out_writes_to_opt_(cwcMax, return) LPWSTR pFaceName)
524 {
525     /* Validate parameters */
526     if (pFaceName && cwcMax <= 0)
527     {
528         /* Set last error and return failure */
529         GdiSetLastError(ERROR_INVALID_PARAMETER);
530         return 0;
531     }
532 
533     /* Forward to kernel */
534     return NtGdiGetTextFaceW(hdc, cwcMax, pFaceName, FALSE);
535 }
536 
537 
538 /*
539  * @implemented
540  */
541 INT
542 WINAPI
543 GetTextFaceA(
544     _In_ HDC hdc,
545     _In_ INT cchMax,
546     _Out_writes_to_opt_(cchMax, return) LPSTR lpName)
547 {
548     INT res;
549     LPWSTR nameW;
550 
551     /* Validate parameters */
552     if (lpName && cchMax <= 0)
553     {
554         /* Set last error and return failure */
555         GdiSetLastError(ERROR_INVALID_PARAMETER);
556         return 0;
557     }
558 
559     res = GetTextFaceW(hdc, 0, NULL);
560     nameW = HeapAlloc( GetProcessHeap(), 0, res * 2 );
561     if (nameW == NULL)
562     {
563         return 0;
564     }
565 
566     GetTextFaceW( hdc, res, nameW );
567 
568     if (lpName)
569     {
570         if (cchMax && !WideCharToMultiByte( CP_ACP, 0, nameW, -1, lpName, cchMax, NULL, NULL))
571             lpName[cchMax-1] = 0;
572         res = strlen(lpName);
573     }
574     else
575     {
576         res = WideCharToMultiByte( CP_ACP, 0, nameW, -1, NULL, 0, NULL, NULL);
577     }
578 
579     HeapFree( GetProcessHeap(), 0, nameW );
580     return res;
581 }
582 
583 
584 /*
585  * @implemented
586  */
587 INT
588 WINAPI
589 GetTextFaceAliasW(
590     _In_ HDC hdc,
591     _In_ INT cwcMax,
592     _Out_writes_to_opt_(cwcMax, return) LPWSTR pszOut)
593 {
594     if (pszOut && !cwcMax)
595     {
596         GdiSetLastError(ERROR_INVALID_PARAMETER);
597         return 0;
598     }
599 
600     return NtGdiGetTextFaceW(hdc, cwcMax, pszOut, TRUE);
601 }
602 
603 
604 BOOL
605 WINAPI
606 GetFontResourceInfoW(
607     _In_z_ LPCWSTR lpFileName,
608     _Inout_ DWORD *pdwBufSize,
609     _Out_writes_to_opt_(*pdwBufSize, 1) PVOID lpBuffer,
610     _In_ DWORD dwType)
611 {
612     BOOL bRet;
613     UNICODE_STRING NtFileName;
614 
615     DPRINT("GetFontResourceInfoW: dwType = %lu\n", dwType);
616 
617     if (!lpFileName || !pdwBufSize)
618     {
619         SetLastError(ERROR_INVALID_PARAMETER);
620         return FALSE;
621     }
622 
623     if (!RtlDosPathNameToNtPathName_U(lpFileName,
624                                       &NtFileName,
625                                       NULL,
626                                       NULL))
627     {
628         SetLastError(ERROR_PATH_NOT_FOUND);
629         return FALSE;
630     }
631 
632     bRet = NtGdiGetFontResourceInfoInternalW(
633                NtFileName.Buffer,
634                (NtFileName.Length / sizeof(WCHAR)) + 1,
635                1,
636                *pdwBufSize,
637                pdwBufSize,
638                lpBuffer,
639                dwType);
640 
641     RtlFreeHeap(RtlGetProcessHeap(), 0, NtFileName.Buffer);
642 
643     return bRet;
644 }
645 
646 
647 /*
648  * @unimplemented
649  */
650 INT
651 WINAPI
652 SetTextCharacterExtra(
653     _In_ HDC hdc,
654     _In_ INT nCharExtra)
655 {
656     PDC_ATTR pdcattr;
657     INT nOldCharExtra;
658 
659     if (nCharExtra == 0x80000000)
660     {
661         SetLastError(ERROR_INVALID_PARAMETER);
662         return 0x80000000;
663     }
664 
665     if (GDI_HANDLE_GET_TYPE(hdc) == GDILoObjType_LO_METADC16_TYPE)
666     {
667         HANDLE_METADC(INT, SetTextCharacterExtra, 0x80000000, hdc, nCharExtra);
668     }
669 
670     /* Get the DC attribute */
671     pdcattr = GdiGetDcAttr(hdc);
672     if (pdcattr == NULL)
673     {
674         SetLastError(ERROR_INVALID_PARAMETER);
675         return 0x8000000;
676     }
677 
678     if (NtCurrentTeb()->GdiTebBatch.HDC == hdc)
679     {
680         if (pdcattr->ulDirty_ & DC_FONTTEXT_DIRTY)
681         {
682             NtGdiFlush(); // Sync up pdcattr from Kernel space.
683             pdcattr->ulDirty_ &= ~(DC_MODE_DIRTY|DC_FONTTEXT_DIRTY);
684         }
685     }
686 
687     nOldCharExtra = pdcattr->lTextExtra;
688     pdcattr->lTextExtra = nCharExtra;
689     return nOldCharExtra;
690 }
691 
692 /*
693  * @implemented
694  *
695  */
696 UINT
697 WINAPI
698 GetTextAlign(
699     _In_ HDC hdc)
700 {
701     PDC_ATTR pdcattr;
702 
703     /* Get the DC attribute */
704     pdcattr = GdiGetDcAttr(hdc);
705     if (pdcattr == NULL)
706     {
707         /* Do not set LastError here! */
708         return GDI_ERROR;
709     }
710 
711     return pdcattr->lTextAlign;
712 }
713 
714 
715 /*
716  * @implemented
717  *
718  */
719 COLORREF
720 WINAPI
721 GetTextColor(
722     _In_ HDC hdc)
723 {
724     PDC_ATTR pdcattr;
725 
726     /* Get the DC attribute */
727     pdcattr = GdiGetDcAttr(hdc);
728     if (pdcattr == NULL)
729     {
730         /* Do not set LastError here! */
731         return CLR_INVALID;
732     }
733 
734     return pdcattr->ulForegroundClr;
735 }
736 
737 
738 /*
739  * @unimplemented
740  */
741 UINT
742 WINAPI
743 SetTextAlign(
744     _In_ HDC hdc,
745     _In_ UINT fMode)
746 {
747     PDC_ATTR pdcattr;
748     UINT fOldMode;
749 
750     HANDLE_METADC(BOOL, SetTextAlign, GDI_ERROR, hdc, fMode);
751 
752     /* Get the DC attribute */
753     pdcattr = GdiGetDcAttr(hdc);
754     if (pdcattr == NULL)
755     {
756         SetLastError(ERROR_INVALID_PARAMETER);
757         return GDI_ERROR;
758     }
759 
760 
761     fOldMode = pdcattr->lTextAlign;
762     pdcattr->lTextAlign = fMode; // Raw
763     if (pdcattr->dwLayout & LAYOUT_RTL)
764     {
765         if ((fMode & TA_CENTER) != TA_CENTER) fMode ^= TA_RIGHT;
766     }
767 
768     pdcattr->flTextAlign = fMode & TA_MASK;
769     return fOldMode;
770 }
771 
772 
773 /*
774  * @implemented
775  */
776 COLORREF
777 WINAPI
778 SetTextColor(
779     _In_ HDC hdc,
780     _In_ COLORREF crColor)
781 {
782     PDC_ATTR pdcattr;
783     COLORREF crOldColor;
784 
785     HANDLE_METADC(COLORREF, SetTextColor, CLR_INVALID, hdc, crColor);
786 
787     pdcattr = GdiGetDcAttr(hdc);
788     if (pdcattr == NULL)
789     {
790         SetLastError(ERROR_INVALID_PARAMETER);
791         return CLR_INVALID;
792     }
793 
794     crOldColor = (COLORREF) pdcattr->ulForegroundClr;
795     pdcattr->ulForegroundClr = (ULONG)crColor;
796 
797     if (pdcattr->crForegroundClr != crColor)
798     {
799         pdcattr->ulDirty_ |= (DIRTY_TEXT|DIRTY_LINE|DIRTY_FILL);
800         pdcattr->crForegroundClr = crColor;
801     }
802 
803     return crOldColor;
804 }
805 
806 /*
807  * @implemented
808  */
809 BOOL
810 WINAPI
811 SetTextJustification(
812     _In_ HDC hdc,
813     _In_ INT nBreakExtra,
814     _In_ INT nBreakCount)
815 {
816     PDC_ATTR pdcattr;
817 
818     if (GDI_HANDLE_GET_TYPE(hdc) == GDILoObjType_LO_METADC16_TYPE)
819     {
820         HANDLE_METADC(BOOL, SetTextJustification, FALSE, hdc, nBreakExtra, nBreakCount);
821     }
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 
832     if (NtCurrentTeb()->GdiTebBatch.HDC == hdc)
833     {
834         if (pdcattr->ulDirty_ & DC_FONTTEXT_DIRTY)
835         {
836             NtGdiFlush(); // Sync up pdcattr from Kernel space.
837             pdcattr->ulDirty_ &= ~(DC_MODE_DIRTY|DC_FONTTEXT_DIRTY);
838         }
839     }
840 
841     pdcattr->cBreak = nBreakCount;
842     pdcattr->lBreakExtra = nBreakExtra;
843     return TRUE;
844 }
845 
846 /*
847  * @implemented
848  */
849 UINT
850 WINAPI
851 GetStringBitmapA(
852     _In_ HDC hdc,
853     _In_ LPSTR psz,
854     _In_ BOOL bDoCall,
855     _In_ UINT cj,
856     _Out_writes_(cj) BYTE *lpSB)
857 {
858 
859     NTSTATUS Status;
860     PWSTR pwsz;
861     UINT uResult = 0;
862 
863     if (!bDoCall)
864     {
865         return 0;
866     }
867 
868     Status = HEAP_strdupA2W(&pwsz, psz);
869     if (!NT_SUCCESS(Status))
870     {
871         SetLastError (RtlNtStatusToDosError(Status));
872     }
873     else
874     {
875         uResult = NtGdiGetStringBitmapW(hdc, pwsz, 1, lpSB, cj);
876         HEAP_free(pwsz);
877     }
878 
879     return uResult;
880 
881 }
882 
883 /*
884  * @implemented
885  */
886 UINT
887 WINAPI
888 GetStringBitmapW(
889     _In_ HDC hdc,
890     _In_ LPWSTR pwsz,
891     _In_ BOOL bDoCall,
892     _In_ UINT cj,
893     _Out_writes_(cj) BYTE *lpSB)
894 {
895     if (!bDoCall)
896     {
897         return 0;
898     }
899 
900     return NtGdiGetStringBitmapW(hdc, pwsz, 1, lpSB, cj);
901 
902 }
903 
904 /*
905  * @implemented
906  */
907 BOOL
908 WINAPI
909 GetETM(
910     _In_ HDC hdc,
911     _Out_ EXTTEXTMETRIC *petm)
912 {
913     BOOL bResult;
914 
915     bResult = NtGdiGetETM(hdc, petm);
916 
917     if (bResult && petm)
918     {
919         petm->emKernPairs = (WORD)GetKerningPairsA(hdc, 0, 0);
920     }
921 
922     return bResult;
923 }
924