xref: /reactos/win32ss/gdi/gdi32/objects/text.c (revision 80733143)
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     if (!(fuOptions & (ETO_GLYPH_INDEX | ETO_IGNORELANGUAGE)))
504     {
505         if (LoadLPK(LPK_ETO))
506             return LpkExtTextOut(hdc, x, y, fuOptions, lprc, lpString, cwc , lpDx, 0);
507     }
508 
509     return NtGdiExtTextOutW(hdc,
510                             x,
511                             y,
512                             fuOptions,
513                             (LPRECT)lprc,
514                             (LPWSTR)lpString,
515                             cwc,
516                             (LPINT)lpDx,
517                             0);
518 }
519 
520 
521 /*
522  * @implemented
523  */
524 INT
525 WINAPI
526 GetTextFaceW(
527     _In_ HDC hdc,
528     _In_ INT cwcMax,
529     _Out_writes_to_opt_(cwcMax, return) LPWSTR pFaceName)
530 {
531     /* Validate parameters */
532     if (pFaceName && cwcMax <= 0)
533     {
534         /* Set last error and return failure */
535         GdiSetLastError(ERROR_INVALID_PARAMETER);
536         return 0;
537     }
538 
539     /* Forward to kernel */
540     return NtGdiGetTextFaceW(hdc, cwcMax, pFaceName, FALSE);
541 }
542 
543 
544 /*
545  * @implemented
546  */
547 INT
548 WINAPI
549 GetTextFaceA(
550     _In_ HDC hdc,
551     _In_ INT cchMax,
552     _Out_writes_to_opt_(cchMax, return) LPSTR lpName)
553 {
554     INT res;
555     LPWSTR nameW;
556 
557     /* Validate parameters */
558     if (lpName && cchMax <= 0)
559     {
560         /* Set last error and return failure */
561         GdiSetLastError(ERROR_INVALID_PARAMETER);
562         return 0;
563     }
564 
565     res = GetTextFaceW(hdc, 0, NULL);
566     nameW = HeapAlloc( GetProcessHeap(), 0, res * 2 );
567     if (nameW == NULL)
568     {
569         return 0;
570     }
571 
572     GetTextFaceW( hdc, res, nameW );
573 
574     if (lpName)
575     {
576         if (cchMax && !WideCharToMultiByte( CP_ACP, 0, nameW, -1, lpName, cchMax, NULL, NULL))
577             lpName[cchMax-1] = 0;
578         res = strlen(lpName);
579     }
580     else
581     {
582         res = WideCharToMultiByte( CP_ACP, 0, nameW, -1, NULL, 0, NULL, NULL);
583     }
584 
585     HeapFree( GetProcessHeap(), 0, nameW );
586     return res;
587 }
588 
589 
590 /*
591  * @implemented
592  */
593 INT
594 WINAPI
595 GetTextFaceAliasW(
596     _In_ HDC hdc,
597     _In_ INT cwcMax,
598     _Out_writes_to_opt_(cwcMax, return) LPWSTR pszOut)
599 {
600     if (pszOut && !cwcMax)
601     {
602         GdiSetLastError(ERROR_INVALID_PARAMETER);
603         return 0;
604     }
605 
606     return NtGdiGetTextFaceW(hdc, cwcMax, pszOut, TRUE);
607 }
608 
609 
610 BOOL
611 WINAPI
612 GetFontResourceInfoW(
613     _In_z_ LPCWSTR lpFileName,
614     _Inout_ DWORD *pdwBufSize,
615     _Out_writes_to_opt_(*pdwBufSize, 1) PVOID lpBuffer,
616     _In_ DWORD dwType)
617 {
618     BOOL bRet;
619     UNICODE_STRING NtFileName;
620 
621     DPRINT("GetFontResourceInfoW: dwType = %lu\n", dwType);
622 
623     if (!lpFileName || !pdwBufSize)
624     {
625         SetLastError(ERROR_INVALID_PARAMETER);
626         return FALSE;
627     }
628 
629     if (!RtlDosPathNameToNtPathName_U(lpFileName,
630                                       &NtFileName,
631                                       NULL,
632                                       NULL))
633     {
634         SetLastError(ERROR_PATH_NOT_FOUND);
635         return FALSE;
636     }
637 
638     bRet = NtGdiGetFontResourceInfoInternalW(
639                NtFileName.Buffer,
640                (NtFileName.Length / sizeof(WCHAR)) + 1,
641                1,
642                *pdwBufSize,
643                pdwBufSize,
644                lpBuffer,
645                dwType);
646 
647     RtlFreeHeap(RtlGetProcessHeap(), 0, NtFileName.Buffer);
648 
649     return bRet;
650 }
651 
652 
653 /*
654  * @unimplemented
655  */
656 INT
657 WINAPI
658 SetTextCharacterExtra(
659     _In_ HDC hdc,
660     _In_ INT nCharExtra)
661 {
662     PDC_ATTR pdcattr;
663     INT nOldCharExtra;
664 
665     if (nCharExtra == 0x80000000)
666     {
667         SetLastError(ERROR_INVALID_PARAMETER);
668         return 0x80000000;
669     }
670 
671     if (GDI_HANDLE_GET_TYPE(hdc) == GDILoObjType_LO_METADC16_TYPE)
672     {
673         HANDLE_METADC(INT, SetTextCharacterExtra, 0x80000000, hdc, nCharExtra);
674     }
675 
676     /* Get the DC attribute */
677     pdcattr = GdiGetDcAttr(hdc);
678     if (pdcattr == NULL)
679     {
680         SetLastError(ERROR_INVALID_PARAMETER);
681         return 0x8000000;
682     }
683 
684     if (NtCurrentTeb()->GdiTebBatch.HDC == hdc)
685     {
686         if (pdcattr->ulDirty_ & DC_FONTTEXT_DIRTY)
687         {
688             NtGdiFlush(); // Sync up pdcattr from Kernel space.
689             pdcattr->ulDirty_ &= ~(DC_MODE_DIRTY|DC_FONTTEXT_DIRTY);
690         }
691     }
692 
693     nOldCharExtra = pdcattr->lTextExtra;
694     pdcattr->lTextExtra = nCharExtra;
695     return nOldCharExtra;
696 }
697 
698 /*
699  * @implemented
700  *
701  */
702 UINT
703 WINAPI
704 GetTextAlign(
705     _In_ HDC hdc)
706 {
707     PDC_ATTR pdcattr;
708 
709     /* Get the DC attribute */
710     pdcattr = GdiGetDcAttr(hdc);
711     if (pdcattr == NULL)
712     {
713         /* Do not set LastError here! */
714         return GDI_ERROR;
715     }
716 
717     return pdcattr->lTextAlign;
718 }
719 
720 
721 /*
722  * @implemented
723  *
724  */
725 COLORREF
726 WINAPI
727 GetTextColor(
728     _In_ HDC hdc)
729 {
730     PDC_ATTR pdcattr;
731 
732     /* Get the DC attribute */
733     pdcattr = GdiGetDcAttr(hdc);
734     if (pdcattr == NULL)
735     {
736         /* Do not set LastError here! */
737         return CLR_INVALID;
738     }
739 
740     return pdcattr->ulForegroundClr;
741 }
742 
743 
744 /*
745  * @unimplemented
746  */
747 UINT
748 WINAPI
749 SetTextAlign(
750     _In_ HDC hdc,
751     _In_ UINT fMode)
752 {
753     PDC_ATTR pdcattr;
754     UINT fOldMode;
755 
756     HANDLE_METADC(BOOL, SetTextAlign, GDI_ERROR, hdc, fMode);
757 
758     /* Get the DC attribute */
759     pdcattr = GdiGetDcAttr(hdc);
760     if (pdcattr == NULL)
761     {
762         SetLastError(ERROR_INVALID_PARAMETER);
763         return GDI_ERROR;
764     }
765 
766 
767     fOldMode = pdcattr->lTextAlign;
768     pdcattr->lTextAlign = fMode; // Raw
769     if (pdcattr->dwLayout & LAYOUT_RTL)
770     {
771         if ((fMode & TA_CENTER) != TA_CENTER) fMode ^= TA_RIGHT;
772     }
773 
774     pdcattr->flTextAlign = fMode & TA_MASK;
775     return fOldMode;
776 }
777 
778 
779 /*
780  * @implemented
781  */
782 COLORREF
783 WINAPI
784 SetTextColor(
785     _In_ HDC hdc,
786     _In_ COLORREF crColor)
787 {
788     PDC_ATTR pdcattr;
789     COLORREF crOldColor;
790 
791     HANDLE_METADC(COLORREF, SetTextColor, CLR_INVALID, hdc, crColor);
792 
793     pdcattr = GdiGetDcAttr(hdc);
794     if (pdcattr == NULL)
795     {
796         SetLastError(ERROR_INVALID_PARAMETER);
797         return CLR_INVALID;
798     }
799 
800     crOldColor = (COLORREF) pdcattr->ulForegroundClr;
801     pdcattr->ulForegroundClr = (ULONG)crColor;
802 
803     if (pdcattr->crForegroundClr != crColor)
804     {
805         pdcattr->ulDirty_ |= (DIRTY_TEXT|DIRTY_LINE|DIRTY_FILL);
806         pdcattr->crForegroundClr = crColor;
807     }
808 
809     return crOldColor;
810 }
811 
812 /*
813  * @implemented
814  */
815 BOOL
816 WINAPI
817 SetTextJustification(
818     _In_ HDC hdc,
819     _In_ INT nBreakExtra,
820     _In_ INT nBreakCount)
821 {
822     PDC_ATTR pdcattr;
823 
824     if (GDI_HANDLE_GET_TYPE(hdc) == GDILoObjType_LO_METADC16_TYPE)
825     {
826         HANDLE_METADC(BOOL, SetTextJustification, FALSE, hdc, nBreakExtra, nBreakCount);
827     }
828 
829     /* Get the DC attribute */
830     pdcattr = GdiGetDcAttr(hdc);
831     if (pdcattr == NULL)
832     {
833         /* Do not set LastError here! */
834         return GDI_ERROR;
835     }
836 
837 
838     if (NtCurrentTeb()->GdiTebBatch.HDC == hdc)
839     {
840         if (pdcattr->ulDirty_ & DC_FONTTEXT_DIRTY)
841         {
842             NtGdiFlush(); // Sync up pdcattr from Kernel space.
843             pdcattr->ulDirty_ &= ~(DC_MODE_DIRTY|DC_FONTTEXT_DIRTY);
844         }
845     }
846 
847     pdcattr->cBreak = nBreakCount;
848     pdcattr->lBreakExtra = nBreakExtra;
849     return TRUE;
850 }
851 
852 /*
853  * @implemented
854  */
855 UINT
856 WINAPI
857 GetStringBitmapA(
858     _In_ HDC hdc,
859     _In_ LPSTR psz,
860     _In_ BOOL bDoCall,
861     _In_ UINT cj,
862     _Out_writes_(cj) BYTE *lpSB)
863 {
864 
865     NTSTATUS Status;
866     PWSTR pwsz;
867     UINT uResult = 0;
868 
869     if (!bDoCall)
870     {
871         return 0;
872     }
873 
874     Status = HEAP_strdupA2W(&pwsz, psz);
875     if (!NT_SUCCESS(Status))
876     {
877         SetLastError (RtlNtStatusToDosError(Status));
878     }
879     else
880     {
881         uResult = NtGdiGetStringBitmapW(hdc, pwsz, 1, lpSB, cj);
882         HEAP_free(pwsz);
883     }
884 
885     return uResult;
886 
887 }
888 
889 /*
890  * @implemented
891  */
892 UINT
893 WINAPI
894 GetStringBitmapW(
895     _In_ HDC hdc,
896     _In_ LPWSTR pwsz,
897     _In_ BOOL bDoCall,
898     _In_ UINT cj,
899     _Out_writes_(cj) BYTE *lpSB)
900 {
901     if (!bDoCall)
902     {
903         return 0;
904     }
905 
906     return NtGdiGetStringBitmapW(hdc, pwsz, 1, lpSB, cj);
907 
908 }
909 
910 /*
911  * @implemented
912  */
913 BOOL
914 WINAPI
915 GetETM(
916     _In_ HDC hdc,
917     _Out_ EXTTEXTMETRIC *petm)
918 {
919     BOOL bResult;
920 
921     bResult = NtGdiGetETM(hdc, petm);
922 
923     if (bResult && petm)
924     {
925         petm->emKernPairs = (WORD)GetKerningPairsA(hdc, 0, 0);
926     }
927 
928     return bResult;
929 }
930