[WIN32SS] Rewrite font selection code. Patch by Katayama Hirofumi MZ. CORE-6621
[reactos.git] / reactos / win32ss / gdi / gdi32 / objects / text.c
1 #include <precomp.h>
2
3 #define NDEBUG
4 #include <debug.h>
5
6 /*
7 * @implemented
8 */
9 BOOL
10 WINAPI
11 TextOutA(
12 _In_ HDC hdc,
13 _In_ INT nXStart,
14 _In_ INT nYStart,
15 _In_reads_(cchString) LPCSTR lpString,
16 _In_ INT cchString)
17 {
18 ANSI_STRING StringA;
19 UNICODE_STRING StringU;
20 BOOL bResult;
21
22 if (lpString != NULL)
23 {
24 RtlInitAnsiString(&StringA, (LPSTR)lpString);
25 RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
26 }
27 else
28 {
29 StringU.Buffer = NULL;
30 }
31
32 bResult = TextOutW(hdc, nXStart, nYStart, StringU.Buffer, cchString);
33
34 RtlFreeUnicodeString(&StringU);
35 return bResult;
36 }
37
38
39 /*
40 * @implemented
41 */
42 BOOL
43 WINAPI
44 TextOutW(
45 _In_ HDC hdc,
46 _In_ INT nXStart,
47 _In_ INT nYStart,
48 _In_reads_(cchString) LPCWSTR lpString,
49 _In_ INT cchString)
50 {
51 return ExtTextOutW(hdc, nXStart, nYStart, 0, NULL, (LPWSTR)lpString, cchString, NULL);
52 }
53
54
55 /*
56 * @unimplemented
57 */
58 BOOL
59 WINAPI
60 PolyTextOutA(
61 _In_ HDC hdc,
62 _In_reads_(cStrings) const POLYTEXTA *pptxt,
63 _In_ INT cStrings)
64 {
65 for (; cStrings>0; cStrings--, pptxt++)
66 {
67 if (!ExtTextOutA(hdc,
68 pptxt->x,
69 pptxt->y,
70 pptxt->uiFlags,
71 &pptxt->rcl,
72 pptxt->lpstr,
73 pptxt->n,
74 pptxt->pdx))
75 {
76 return FALSE;
77 }
78 }
79
80 return TRUE;
81 }
82
83
84 /*
85 * @unimplemented
86 */
87 BOOL
88 WINAPI
89 PolyTextOutW(
90 _In_ HDC hdc,
91 _In_reads_(cStrings) const POLYTEXTW *pptxt,
92 _In_ INT cStrings)
93 {
94 for (; cStrings>0; cStrings--, pptxt++)
95 {
96 if (!ExtTextOutW(hdc,
97 pptxt->x,
98 pptxt->y,
99 pptxt->uiFlags,
100 &pptxt->rcl,
101 pptxt->lpstr,
102 pptxt->n,
103 pptxt->pdx))
104 {
105 return FALSE;
106 }
107 }
108
109 return TRUE;
110 }
111
112
113 /*
114 * @implemented
115 */
116 DWORD
117 WINAPI
118 GdiGetCodePage(
119 _In_ HDC hdc)
120 {
121 PDC_ATTR pdcattr;
122
123 /* Get the DC attribute */
124 pdcattr = GdiGetDcAttr(hdc);
125 if (pdcattr == NULL)
126 {
127 SetLastError(ERROR_INVALID_PARAMETER);
128 return 0;
129 }
130
131 if (pdcattr->ulDirty_ & DIRTY_CHARSET)
132 return LOWORD(NtGdiGetCharSet(hdc));
133
134 return LOWORD(pdcattr->iCS_CP);
135 }
136
137
138 /*
139 * @unimplemented
140 */
141 INT
142 WINAPI
143 GetTextCharacterExtra(
144 _In_ HDC hdc)
145 {
146 PDC_ATTR pdcattr;
147
148 /* Get the DC attribute */
149 pdcattr = GdiGetDcAttr(hdc);
150 if (pdcattr == NULL)
151 {
152 /* Do not set LastError here! */
153 return 0x8000000;
154 }
155
156 return pdcattr->lTextExtra;
157 }
158
159
160 /*
161 * @implemented
162 */
163 INT
164 WINAPI
165 GetTextCharset(
166 _In_ HDC hdc)
167 {
168 /* MSDN docs say this is equivalent */
169 return NtGdiGetTextCharsetInfo(hdc,NULL,0);
170 }
171
172
173 /*
174 * @implemented
175 */
176 BOOL
177 WINAPI
178 GetTextMetricsA(
179 _In_ HDC hdc,
180 _Out_ LPTEXTMETRICA lptm)
181 {
182 TMW_INTERNAL tmwi;
183
184 if (!NtGdiGetTextMetricsW(hdc, &tmwi, sizeof(TMW_INTERNAL)))
185 {
186 return FALSE;
187 }
188
189 FONT_TextMetricWToA(&tmwi.TextMetric, lptm);
190 return TRUE;
191 }
192
193
194 /*
195 * @implemented
196 */
197 BOOL
198 WINAPI
199 GetTextMetricsW(
200 _In_ HDC hdc,
201 _Out_ LPTEXTMETRICW lptm)
202 {
203 TMW_INTERNAL tmwi;
204
205 if (!NtGdiGetTextMetricsW(hdc, &tmwi, sizeof(TMW_INTERNAL)))
206 {
207 return FALSE;
208 }
209
210 *lptm = tmwi.TextMetric;
211 return TRUE;
212 }
213
214
215 /*
216 * @implemented
217 */
218 BOOL
219 APIENTRY
220 GetTextExtentPointA(
221 _In_ HDC hdc,
222 _In_reads_(cchString) LPCSTR lpString,
223 _In_ INT cchString,
224 _Out_ LPSIZE lpsz)
225 {
226 ANSI_STRING StringA;
227 UNICODE_STRING StringU;
228 BOOL ret;
229
230 RtlInitAnsiString(&StringA, (LPSTR)lpString);
231 RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
232
233 ret = GetTextExtentPointW(hdc, StringU.Buffer, cchString, lpsz);
234
235 RtlFreeUnicodeString(&StringU);
236
237 return ret;
238 }
239
240
241 /*
242 * @implemented
243 */
244 BOOL
245 APIENTRY
246 GetTextExtentPointW(
247 _In_ HDC hdc,
248 _In_reads_(cchString) LPCWSTR lpString,
249 _In_ INT cchString,
250 _Out_ LPSIZE lpsz)
251 {
252 return NtGdiGetTextExtent(hdc, (LPWSTR)lpString, cchString, lpsz, 0);
253 }
254
255
256 /*
257 * @implemented
258 */
259 BOOL
260 WINAPI
261 GetTextExtentExPointW(
262 _In_ HDC hdc,
263 _In_reads_(cchString) LPCWSTR lpszString,
264 _In_ INT cchString,
265 _In_ INT nMaxExtent,
266 _Out_opt_ LPINT lpnFit,
267 _Out_writes_to_opt_(cchString, *lpnFit) LPINT lpnDx,
268 _Out_ LPSIZE lpSize)
269 {
270
271 /* Windows doesn't check nMaxExtent validity in unicode version */
272 if (nMaxExtent < -1)
273 {
274 DPRINT("nMaxExtent is invalid: %d\n", nMaxExtent);
275 }
276
277 return NtGdiGetTextExtentExW (
278 hdc, (LPWSTR)lpszString, cchString, nMaxExtent, (PULONG)lpnFit, (PULONG)lpnDx, lpSize, 0 );
279 }
280
281
282 /*
283 * @implemented
284 */
285 BOOL
286 WINAPI
287 GetTextExtentExPointWPri(
288 _In_ HDC hdc,
289 _In_reads_(cwc) LPWSTR lpwsz,
290 _In_ ULONG cwc,
291 _In_ ULONG dxMax,
292 _Out_opt_ ULONG *pcCh,
293 _Out_writes_to_opt_(cwc, *pcCh) PULONG pdxOut,
294 _In_ LPSIZE psize)
295 {
296 return NtGdiGetTextExtentExW(hdc, lpwsz, cwc, dxMax, pcCh, pdxOut, psize, 0);
297 }
298
299 /*
300 * @implemented
301 */
302 BOOL
303 WINAPI
304 GetTextExtentExPointA(
305 _In_ HDC hdc,
306 _In_reads_(cchString) LPCSTR lpszStr,
307 _In_ INT cchString,
308 _In_ INT nMaxExtent,
309 _Out_opt_ LPINT lpnFit,
310 _Out_writes_to_opt_ (cchString, *lpnFit) LPINT lpnDx,
311 _Out_ LPSIZE lpSize)
312 {
313 NTSTATUS Status;
314 LPWSTR lpszStrW;
315 BOOL bResult = FALSE;
316
317 if (nMaxExtent < -1)
318 {
319 SetLastError(ERROR_INVALID_PARAMETER);
320 return FALSE;
321 }
322
323 Status = HEAP_strdupA2W(&lpszStrW, lpszStr);
324 if (!NT_SUCCESS (Status))
325 {
326 SetLastError(RtlNtStatusToDosError(Status));
327 return FALSE;
328 }
329
330 bResult = NtGdiGetTextExtentExW(hdc,
331 lpszStrW,
332 cchString,
333 nMaxExtent,
334 (PULONG)lpnFit,
335 (PULONG)lpnDx,
336 lpSize,
337 0);
338
339 HEAP_free(lpszStrW);
340
341 return bResult;
342 }
343
344
345 /*
346 * @implemented
347 */
348 BOOL
349 WINAPI
350 GetTextExtentPoint32A(
351 _In_ HDC hdc,
352 _In_reads_(cchString) LPCSTR lpString,
353 _In_ INT cchString,
354 _Out_ LPSIZE lpSize)
355 {
356 ANSI_STRING StringA;
357 UNICODE_STRING StringU;
358 BOOL ret;
359
360 StringA.Buffer = (LPSTR)lpString;
361 StringA.Length = cchString;
362 RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
363
364 ret = GetTextExtentPoint32W(hdc, StringU.Buffer, cchString, lpSize);
365
366 RtlFreeUnicodeString(&StringU);
367
368 return ret;
369 }
370
371
372 /*
373 * @implemented
374 */
375 BOOL
376 WINAPI
377 GetTextExtentPoint32W(
378 _In_ HDC hdc,
379 _In_reads_(cchString) LPCWSTR lpString,
380 _In_ int cchString,
381 _Out_ LPSIZE lpSize)
382 {
383 return NtGdiGetTextExtent(hdc, (LPWSTR)lpString, cchString, lpSize, 0);
384 }
385
386 /*
387 * @implemented
388 */
389 BOOL
390 WINAPI
391 GetTextExtentExPointI(
392 _In_ HDC hdc,
393 _In_reads_(cgi) LPWORD pgiIn,
394 _In_ INT cgi,
395 _In_ INT nMaxExtent,
396 _Out_opt_ LPINT lpnFit,
397 _Out_writes_to_opt_(cwchString, *lpnFit) LPINT lpnDx,
398 _Out_ LPSIZE lpSize)
399 {
400 return NtGdiGetTextExtentExW(hdc,
401 pgiIn,
402 cgi,
403 nMaxExtent,
404 (PULONG)lpnFit,
405 (PULONG)lpnDx,
406 lpSize,
407 GTEF_INDICES);
408 }
409
410 /*
411 * @implemented
412 */
413 BOOL
414 WINAPI
415 GetTextExtentPointI(
416 _In_ HDC hdc,
417 _In_reads_(cgi) LPWORD pgiIn,
418 _In_ int cgi,
419 _Out_ LPSIZE lpSize)
420 {
421 return NtGdiGetTextExtent(hdc, pgiIn, cgi, lpSize, GTEF_INDICES);
422 }
423
424 /*
425 * @implemented
426 */
427 BOOL
428 WINAPI
429 ExtTextOutA(
430 _In_ HDC hdc,
431 _In_ INT x,
432 _In_ INT y,
433 _In_ UINT fuOptions,
434 _In_opt_ const RECT *lprc,
435 _In_reads_opt_(cch) LPCSTR lpString,
436 _In_ UINT cch,
437 _In_reads_opt_(cch) const INT *lpDx)
438 {
439 ANSI_STRING StringA;
440 UNICODE_STRING StringU;
441 BOOL ret;
442
443 RtlInitAnsiString(&StringA, (LPSTR)lpString);
444 RtlAnsiStringToUnicodeString(&StringU, &StringA, TRUE);
445
446 ret = ExtTextOutW(hdc, x, y, fuOptions, lprc, StringU.Buffer, cch, lpDx);
447
448 RtlFreeUnicodeString(&StringU);
449
450 return ret;
451 }
452
453
454 /*
455 * @implemented
456 */
457 BOOL
458 WINAPI
459 ExtTextOutW(
460 _In_ HDC hdc,
461 _In_ INT x,
462 _In_ INT y,
463 _In_ UINT fuOptions,
464 _In_opt_ const RECT *lprc,
465 _In_reads_opt_(cwc) LPCWSTR lpString,
466 _In_ UINT cwc,
467 _In_reads_opt_(cwc) const INT *lpDx)
468 {
469 HANDLE_METADC(BOOL,
470 ExtTextOut,
471 FALSE,
472 hdc,
473 x,
474 y,
475 fuOptions,
476 lprc,
477 lpString,
478 cwc,
479 lpDx);
480
481 return NtGdiExtTextOutW(hdc,
482 x,
483 y,
484 fuOptions,
485 (LPRECT)lprc,
486 (LPWSTR)lpString,
487 cwc,
488 (LPINT)lpDx,
489 0);
490 }
491
492
493 /*
494 * @implemented
495 */
496 INT
497 WINAPI
498 GetTextFaceW(
499 _In_ HDC hdc,
500 _In_ INT cwcMax,
501 _Out_writes_to_opt_(cwcMax, return) LPWSTR pFaceName)
502 {
503 /* Validate parameters */
504 if (pFaceName && cwcMax <= 0)
505 {
506 /* Set last error and return failure */
507 GdiSetLastError(ERROR_INVALID_PARAMETER);
508 return 0;
509 }
510
511 /* Forward to kernel */
512 return NtGdiGetTextFaceW(hdc, cwcMax, pFaceName, FALSE);
513 }
514
515
516 /*
517 * @implemented
518 */
519 INT
520 WINAPI
521 GetTextFaceA(
522 _In_ HDC hdc,
523 _In_ INT cchMax,
524 _Out_writes_to_opt_(cchMax, return) LPSTR lpName)
525 {
526 INT res;
527 LPWSTR nameW;
528
529 /* Validate parameters */
530 if (lpName && cchMax <= 0)
531 {
532 /* Set last error and return failure */
533 GdiSetLastError(ERROR_INVALID_PARAMETER);
534 return 0;
535 }
536
537 res = GetTextFaceW(hdc, 0, NULL);
538 nameW = HeapAlloc( GetProcessHeap(), 0, res * 2 );
539 if (nameW == NULL)
540 {
541 return 0;
542 }
543
544 GetTextFaceW( hdc, res, nameW );
545
546 if (lpName)
547 {
548 if (cchMax && !WideCharToMultiByte( CP_ACP, 0, nameW, -1, lpName, cchMax, NULL, NULL))
549 lpName[cchMax-1] = 0;
550 res = strlen(lpName);
551 }
552 else
553 {
554 res = WideCharToMultiByte( CP_ACP, 0, nameW, -1, NULL, 0, NULL, NULL);
555 }
556
557 HeapFree( GetProcessHeap(), 0, nameW );
558 return res;
559 }
560
561
562 /*
563 * @implemented
564 */
565 INT
566 WINAPI
567 GetTextFaceAliasW(
568 _In_ HDC hdc,
569 _In_ INT cwcMax,
570 _Out_writes_to_opt_(cwcMax, return) LPWSTR pszOut)
571 {
572 if (pszOut && !cwcMax)
573 {
574 GdiSetLastError(ERROR_INVALID_PARAMETER);
575 return 0;
576 }
577
578 return NtGdiGetTextFaceW(hdc, cwcMax, pszOut, TRUE);
579 }
580
581
582 BOOL
583 WINAPI
584 GetFontResourceInfoW(
585 _In_z_ LPCWSTR lpFileName,
586 _Inout_ DWORD *pdwBufSize,
587 _Out_writes_to_opt_(*pdwBufSize, 1) PVOID lpBuffer,
588 _In_ DWORD dwType)
589 {
590 BOOL bRet;
591 UNICODE_STRING NtFileName;
592
593 DPRINT("GetFontResourceInfoW: dwType = %lu\n", dwType);
594
595 if (!lpFileName || !pdwBufSize)
596 {
597 SetLastError(ERROR_INVALID_PARAMETER);
598 return FALSE;
599 }
600
601 if (!RtlDosPathNameToNtPathName_U(lpFileName,
602 &NtFileName,
603 NULL,
604 NULL))
605 {
606 SetLastError(ERROR_PATH_NOT_FOUND);
607 return FALSE;
608 }
609
610 bRet = NtGdiGetFontResourceInfoInternalW(
611 NtFileName.Buffer,
612 (NtFileName.Length / sizeof(WCHAR)) + 1,
613 1,
614 *pdwBufSize,
615 pdwBufSize,
616 lpBuffer,
617 dwType);
618
619 RtlFreeHeap(RtlGetProcessHeap(), 0, NtFileName.Buffer);
620
621 return bRet;
622 }
623
624
625 /*
626 * @unimplemented
627 */
628 INT
629 WINAPI
630 SetTextCharacterExtra(
631 _In_ HDC hdc,
632 _In_ INT nCharExtra)
633 {
634 PDC_ATTR pdcattr;
635 INT nOldCharExtra;
636
637 if (nCharExtra == 0x80000000)
638 {
639 SetLastError(ERROR_INVALID_PARAMETER);
640 return 0x80000000;
641 }
642
643 if (GDI_HANDLE_GET_TYPE(hdc) == GDILoObjType_LO_METADC16_TYPE)
644 {
645 HANDLE_METADC(INT, SetTextCharacterExtra, 0x80000000, hdc, nCharExtra);
646 }
647
648 /* Get the DC attribute */
649 pdcattr = GdiGetDcAttr(hdc);
650 if (pdcattr == NULL)
651 {
652 SetLastError(ERROR_INVALID_PARAMETER);
653 return 0x8000000;
654 }
655
656 if (NtCurrentTeb()->GdiTebBatch.HDC == hdc)
657 {
658 if (pdcattr->ulDirty_ & DC_FONTTEXT_DIRTY)
659 {
660 NtGdiFlush(); // Sync up pdcattr from Kernel space.
661 pdcattr->ulDirty_ &= ~(DC_MODE_DIRTY|DC_FONTTEXT_DIRTY);
662 }
663 }
664
665 nOldCharExtra = pdcattr->lTextExtra;
666 pdcattr->lTextExtra = nCharExtra;
667 return nOldCharExtra;
668 }
669
670 /*
671 * @implemented
672 *
673 */
674 UINT
675 WINAPI
676 GetTextAlign(
677 _In_ HDC hdc)
678 {
679 PDC_ATTR pdcattr;
680
681 /* Get the DC attribute */
682 pdcattr = GdiGetDcAttr(hdc);
683 if (pdcattr == NULL)
684 {
685 /* Do not set LastError here! */
686 return GDI_ERROR;
687 }
688
689 return pdcattr->lTextAlign;
690 }
691
692
693 /*
694 * @implemented
695 *
696 */
697 COLORREF
698 WINAPI
699 GetTextColor(
700 _In_ HDC hdc)
701 {
702 PDC_ATTR pdcattr;
703
704 /* Get the DC attribute */
705 pdcattr = GdiGetDcAttr(hdc);
706 if (pdcattr == NULL)
707 {
708 /* Do not set LastError here! */
709 return CLR_INVALID;
710 }
711
712 return pdcattr->ulForegroundClr;
713 }
714
715
716 /*
717 * @unimplemented
718 */
719 UINT
720 WINAPI
721 SetTextAlign(
722 _In_ HDC hdc,
723 _In_ UINT fMode)
724 {
725 PDC_ATTR pdcattr;
726 UINT fOldMode;
727
728 HANDLE_METADC(BOOL, SetTextAlign, GDI_ERROR, hdc, fMode);
729
730 /* Get the DC attribute */
731 pdcattr = GdiGetDcAttr(hdc);
732 if (pdcattr == NULL)
733 {
734 SetLastError(ERROR_INVALID_PARAMETER);
735 return GDI_ERROR;
736 }
737
738
739 fOldMode = pdcattr->lTextAlign;
740 pdcattr->lTextAlign = fMode; // Raw
741 if (pdcattr->dwLayout & LAYOUT_RTL)
742 {
743 if ((fMode & TA_CENTER) != TA_CENTER) fMode ^= TA_RIGHT;
744 }
745
746 pdcattr->flTextAlign = fMode & TA_MASK;
747 return fOldMode;
748 }
749
750
751 /*
752 * @implemented
753 */
754 COLORREF
755 WINAPI
756 SetTextColor(
757 _In_ HDC hdc,
758 _In_ COLORREF crColor)
759 {
760 PDC_ATTR pdcattr;
761 COLORREF crOldColor;
762
763 HANDLE_METADC(COLORREF, SetTextColor, CLR_INVALID, hdc, crColor);
764
765 pdcattr = GdiGetDcAttr(hdc);
766 if (pdcattr == NULL)
767 {
768 SetLastError(ERROR_INVALID_PARAMETER);
769 return CLR_INVALID;
770 }
771
772 crOldColor = (COLORREF) pdcattr->ulForegroundClr;
773 pdcattr->ulForegroundClr = (ULONG)crColor;
774
775 if (pdcattr->crForegroundClr != crColor)
776 {
777 pdcattr->ulDirty_ |= (DIRTY_TEXT|DIRTY_LINE|DIRTY_FILL);
778 pdcattr->crForegroundClr = crColor;
779 }
780
781 return crOldColor;
782 }
783
784 /*
785 * @implemented
786 */
787 BOOL
788 WINAPI
789 SetTextJustification(
790 _In_ HDC hdc,
791 _In_ INT nBreakExtra,
792 _In_ INT nBreakCount)
793 {
794 PDC_ATTR pdcattr;
795
796 if (GDI_HANDLE_GET_TYPE(hdc) == GDILoObjType_LO_METADC16_TYPE)
797 {
798 HANDLE_METADC(BOOL, SetTextJustification, FALSE, hdc, nBreakExtra, nBreakCount);
799 }
800
801 /* Get the DC attribute */
802 pdcattr = GdiGetDcAttr(hdc);
803 if (pdcattr == NULL)
804 {
805 /* Do not set LastError here! */
806 return GDI_ERROR;
807 }
808
809
810 if (NtCurrentTeb()->GdiTebBatch.HDC == hdc)
811 {
812 if (pdcattr->ulDirty_ & DC_FONTTEXT_DIRTY)
813 {
814 NtGdiFlush(); // Sync up pdcattr from Kernel space.
815 pdcattr->ulDirty_ &= ~(DC_MODE_DIRTY|DC_FONTTEXT_DIRTY);
816 }
817 }
818
819 pdcattr->cBreak = nBreakCount;
820 pdcattr->lBreakExtra = nBreakExtra;
821 return TRUE;
822 }
823
824 /*
825 * @implemented
826 */
827 UINT
828 WINAPI
829 GetStringBitmapA(
830 _In_ HDC hdc,
831 _In_ LPSTR psz,
832 _In_ BOOL bDoCall,
833 _In_ UINT cj,
834 _Out_writes_(cj) BYTE *lpSB)
835 {
836
837 NTSTATUS Status;
838 PWSTR pwsz;
839 UINT uResult = 0;
840
841 if (!bDoCall)
842 {
843 return 0;
844 }
845
846 Status = HEAP_strdupA2W(&pwsz, psz);
847 if (!NT_SUCCESS(Status))
848 {
849 SetLastError (RtlNtStatusToDosError(Status));
850 }
851 else
852 {
853 uResult = NtGdiGetStringBitmapW(hdc, pwsz, 1, lpSB, cj);
854 HEAP_free(pwsz);
855 }
856
857 return uResult;
858
859 }
860
861 /*
862 * @implemented
863 */
864 UINT
865 WINAPI
866 GetStringBitmapW(
867 _In_ HDC hdc,
868 _In_ LPWSTR pwsz,
869 _In_ BOOL bDoCall,
870 _In_ UINT cj,
871 _Out_writes_(cj) BYTE *lpSB)
872 {
873 if (!bDoCall)
874 {
875 return 0;
876 }
877
878 return NtGdiGetStringBitmapW(hdc, pwsz, 1, lpSB, cj);
879
880 }
881
882 /*
883 * @implemented
884 */
885 BOOL
886 WINAPI
887 GetETM(
888 _In_ HDC hdc,
889 _Out_ EXTTEXTMETRIC *petm)
890 {
891 BOOL bResult;
892
893 bResult = NtGdiGetETM(hdc, petm);
894
895 if (bResult && petm)
896 {
897 petm->emKernPairs = (WORD)GetKerningPairsA(hdc, 0, 0);
898 }
899
900 return bResult;
901 }