Release 971221
[wine] / objects / text.c
1 /*
2  * text functions
3  *
4  * Copyright 1993, 1994 Alexandre Julliard
5  *
6  */
7
8 #include <stdlib.h>
9 #include "windows.h"
10 #include "dc.h"
11 #include "gdi.h"
12 #include "heap.h"
13 #include "stddebug.h"
14 /* #define DEBUG_TEXT */
15 #include "debug.h"
16 #include "cache.h"
17
18 #define TAB     9
19 #define LF     10
20 #define CR     13
21 #define SPACE  32
22 #define PREFIX 38
23
24 #define SWAP_INT(a,b)  { int t = a; a = b; b = t; }
25
26 static int tabstop = 8;
27 static int tabwidth;
28 static int spacewidth;
29 static int prefix_offset;
30
31 static const char *TEXT_NextLine( HDC16 hdc, const char *str, int *count,
32                                   char *dest, int *len, int width, WORD format)
33 {
34     /* Return next line of text from a string.
35      * 
36      * hdc - handle to DC.
37      * str - string to parse into lines.
38      * count - length of str.
39      * dest - destination in which to return line.
40      * len - length of resultant line in dest in chars.
41      * width - maximum width of line in pixels.
42      * format - format type passed to DrawText.
43      *
44      * Returns pointer to next char in str after end of the line
45      * or NULL if end of str reached.
46      */
47
48     int i = 0, j = 0, k;
49     int plen = 0;
50     int numspaces;
51     SIZE16 size;
52     int lasttab = 0;
53     int wb_i = 0, wb_j = 0, wb_count = 0;
54
55     while (*count)
56     {
57         switch (str[i])
58         {
59         case CR:
60         case LF:
61             if (!(format & DT_SINGLELINE))
62             {
63                 if ((*count > 1) && (str[i] == CR) && (str[i+1] == LF))
64                 {
65                     (*count)--;
66                     i++;
67                 }
68                 i++;
69                 *len = j;
70                 (*count)--;
71                 return (&str[i]);
72             }
73             dest[j++] = str[i++];
74             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
75                 (format & DT_WORDBREAK))
76             {
77                 if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
78                     return NULL;
79                 plen += size.cx;
80             }
81             break;
82             
83         case PREFIX:
84             if (!(format & DT_NOPREFIX) && *count > 1)
85                 {
86                 if (str[++i] == PREFIX)
87                     (*count)--;
88                 else {
89                     prefix_offset = j;
90                     break;
91                 }
92             }
93             dest[j++] = str[i++];
94             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
95                 (format & DT_WORDBREAK))
96             {
97                 if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
98                     return NULL;
99                 plen += size.cx;
100             }
101             break;
102             
103         case TAB:
104             if (format & DT_EXPANDTABS)
105             {
106                 wb_i = ++i;
107                 wb_j = j;
108                 wb_count = *count;
109
110                 if (!GetTextExtentPoint16(hdc, &dest[lasttab], j - lasttab,
111                                                                  &size))
112                     return NULL;
113
114                 numspaces = (tabwidth - size.cx) / spacewidth;
115                 for (k = 0; k < numspaces; k++)
116                     dest[j++] = SPACE;
117                 plen += tabwidth - size.cx;
118                 lasttab = wb_j + numspaces;
119             }
120             else
121             {
122                 dest[j++] = str[i++];
123                 if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
124                     (format & DT_WORDBREAK))
125                 {
126                     if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
127                         return NULL;
128                     plen += size.cx;
129                 }
130             }
131             break;
132
133         case SPACE:
134             dest[j++] = str[i++];
135             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
136                 (format & DT_WORDBREAK))
137             {
138                 wb_i = i;
139                 wb_j = j - 1;
140                 wb_count = *count;
141                 if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
142                     return NULL;
143                 plen += size.cx;
144             }
145             break;
146
147         default:
148             dest[j++] = str[i++];
149             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
150                 (format & DT_WORDBREAK))
151             {
152                 if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
153                     return NULL;
154                 plen += size.cx;
155             }
156         }
157
158         (*count)--;
159         if (!(format & DT_NOCLIP) || (format & DT_WORDBREAK))
160         {
161             if (plen > width)
162             {
163                 if (format & DT_WORDBREAK)
164                 {
165                     if (wb_j)
166                     {
167                         *len = wb_j;
168                         *count = wb_count - 1;
169                         return (&str[wb_i]);
170                     }
171                 }
172                 else
173                 {
174                     *len = j;
175                     return (&str[i]);
176                 }
177             }
178         }
179     }
180     
181     *len = j;
182     return NULL;
183 }
184
185
186 /***********************************************************************
187  *           DrawText16    (USER.85)
188  */
189 INT16 WINAPI DrawText16( HDC16 hdc, LPCSTR str, INT16 i_count,
190                          LPRECT16 rect, UINT16 flags )
191 {
192     SIZE16 size;
193     const char *strPtr;
194     static char line[1024];
195     int len, lh, count=i_count;
196     int prefix_x = 0;
197     int prefix_end = 0;
198     TEXTMETRIC16 tm;
199     int x = rect->left, y = rect->top;
200     int width = rect->right - rect->left;
201     int max_width = 0;
202
203     dprintf_text(stddeb,"DrawText: '%s', %d , [(%d,%d),(%d,%d)]\n", str,
204                  count, rect->left, rect->top, rect->right, rect->bottom);
205     
206     if (count == -1) count = strlen(str);
207     strPtr = str;
208
209     GetTextMetrics16(hdc, &tm);
210     if (flags & DT_EXTERNALLEADING)
211         lh = tm.tmHeight + tm.tmExternalLeading;
212     else
213         lh = tm.tmHeight;
214
215     if (flags & DT_TABSTOP)
216         tabstop = flags >> 8;
217
218     if (flags & DT_EXPANDTABS)
219     {
220         GetTextExtentPoint16(hdc, " ", 1, &size);
221         spacewidth = size.cx;
222         GetTextExtentPoint16(hdc, "o", 1, &size);
223         tabwidth = size.cx * tabstop;
224     }
225
226     if (flags & DT_CALCRECT) flags |= DT_NOCLIP;
227
228     do
229     {
230         prefix_offset = -1;
231         strPtr = TEXT_NextLine(hdc, strPtr, &count, line, &len, width, flags);
232
233         if (prefix_offset != -1)
234         {
235             GetTextExtentPoint16(hdc, line, prefix_offset, &size);
236             prefix_x = size.cx;
237             GetTextExtentPoint16(hdc, line, prefix_offset + 1, &size);
238             prefix_end = size.cx - 1;
239         }
240
241         if (!GetTextExtentPoint16(hdc, line, len, &size)) return 0;
242         if (flags & DT_CENTER) x = (rect->left + rect->right -
243                                     size.cx) / 2;
244         else if (flags & DT_RIGHT) x = rect->right - size.cx;
245
246         if (flags & DT_SINGLELINE)
247         {
248             if (flags & DT_VCENTER) y = rect->top + 
249                 (rect->bottom - rect->top) / 2 - size.cy / 2;
250             else if (flags & DT_BOTTOM) y = rect->bottom - size.cy;
251         }
252         if (!(flags & DT_CALCRECT))
253         {
254             if (!ExtTextOut16(hdc, x, y, (flags & DT_NOCLIP) ? 0 : ETO_CLIPPED,
255                               rect, line, len, NULL )) return 0;
256             if (prefix_offset != -1)
257             {
258                 HPEN32 hpen = CreatePen32( PS_SOLID, 1, GetTextColor32(hdc) );
259                 HPEN32 oldPen = SelectObject32( hdc, hpen );
260                 MoveTo(hdc, x + prefix_x, y + tm.tmAscent + 1 );
261                 LineTo32(hdc, x + prefix_end + 1, y + tm.tmAscent + 1 );
262                 SelectObject32( hdc, oldPen );
263                 DeleteObject32( hpen );
264             }
265         }
266         else if (size.cx > max_width)
267             max_width = size.cx;
268
269         y += lh;
270         if (strPtr)
271         {
272             if (!(flags & DT_NOCLIP))
273             {
274                 if (y > rect->bottom - lh)
275                     break;
276             }
277         }
278     }
279     while (strPtr);
280     if (flags & DT_CALCRECT)
281     {
282         rect->right = rect->left + max_width;
283         rect->bottom = y;
284     }
285     return y - rect->top;
286 }
287
288
289 /***********************************************************************
290  *           DrawText32A    (USER32.163)
291  */
292 INT32 WINAPI DrawText32A( HDC32 hdc, LPCSTR str, INT32 count,
293                           LPRECT32 rect, UINT32 flags )
294 {
295     RECT16 rect16;
296     INT16 ret;
297
298     if (!rect)
299         return DrawText16( (HDC16)hdc, str, (INT16)count, NULL, (UINT16)flags);
300     CONV_RECT32TO16( rect, &rect16 );
301     ret = DrawText16( (HDC16)hdc, str, (INT16)count, &rect16, (UINT16)flags );
302     CONV_RECT16TO32( &rect16, rect );
303     return ret;
304 }
305
306
307 /***********************************************************************
308  *           DrawText32W    (USER32.166)
309  */
310 INT32 WINAPI DrawText32W( HDC32 hdc, LPCWSTR str, INT32 count,
311                           LPRECT32 rect, UINT32 flags )
312 {
313     LPSTR p = HEAP_strdupWtoA( GetProcessHeap(), 0, str );
314     INT32 ret = DrawText32A( hdc, p, count, rect, flags );
315     HeapFree( GetProcessHeap(), 0, p );
316     return ret;
317 }
318
319 /***********************************************************************
320  *           DrawTextEx32A    (USER32.164)
321  */
322 INT32 DrawTextEx32A( HDC32 hdc, LPCSTR str, INT32 count,
323                      LPRECT32 rect, UINT32 flags, LPDRAWTEXTPARAMS dtp )
324 {
325     fprintf(stderr,"DrawTextEx32A(%d,'%s',%d,%p,0x%08x,%p)\n",
326         hdc,str,count,rect,flags,dtp
327     );
328     /*FIXME: ignores extended functionality ... */
329     return DrawText32A(hdc,str,count,rect,flags);
330 }
331
332 /***********************************************************************
333  *           DrawTextEx32W    (USER32.165)
334  */
335 INT32 DrawTextEx32W( HDC32 hdc, LPCWSTR str, INT32 count,
336                      LPRECT32 rect, UINT32 flags, LPDRAWTEXTPARAMS dtp )
337 {
338     fprintf(stderr,"DrawTextEx32A(%d,%p,%d,%p,0x%08x,%p)\n",
339         hdc,str,count,rect,flags,dtp
340     );
341     /*FIXME: ignores extended functionality ... */
342     return DrawText32W(hdc,str,count,rect,flags);
343 }
344
345 /***********************************************************************
346  *           ExtTextOut16    (GDI.351)
347  */
348 BOOL16 WINAPI ExtTextOut16( HDC16 hdc, INT16 x, INT16 y, UINT16 flags,
349                             const RECT16 *lprect, LPCSTR str, UINT16 count,
350                             const INT16 *lpDx )
351 {
352     BOOL32      ret;
353     int         i;
354     RECT32      rect32;
355     LPINT32     lpdx32 = NULL;
356
357     if (lpDx) lpdx32 = (LPINT32)HEAP_xalloc( GetProcessHeap(), 0,
358                                              sizeof(INT32)*count );
359     if (lprect) CONV_RECT16TO32(lprect,&rect32);
360     if (lpdx32) for (i=count;i--;) lpdx32[i]=lpDx[i];
361     ret = ExtTextOut32A(hdc,x,y,flags,lprect?&rect32:NULL,str,count,lpdx32);
362     if (lpdx32) HeapFree( GetProcessHeap(), 0, lpdx32 );
363     return ret;
364
365
366 }
367
368
369 /***********************************************************************
370  *           ExtTextOut32A    (GDI32.98)
371  */
372 BOOL32 WINAPI ExtTextOut32A( HDC32 hdc, INT32 x, INT32 y, UINT32 flags,
373                              const RECT32 *lprect, LPCSTR str, UINT32 count,
374                              const INT32 *lpDx )
375 {
376     DC * dc = DC_GetDCPtr( hdc );
377     return dc && dc->funcs->pExtTextOut && 
378            dc->funcs->pExtTextOut(dc,x,y,flags,lprect,str,count,lpDx);
379 }
380
381
382 /***********************************************************************
383  *           ExtTextOut32W    (GDI32.99)
384  */
385 BOOL32 WINAPI ExtTextOut32W( HDC32 hdc, INT32 x, INT32 y, UINT32 flags,
386                              const RECT32 *lprect, LPCWSTR str, UINT32 count,
387                              const INT32 *lpDx )
388 {
389     LPSTR p = HEAP_strdupWtoA( GetProcessHeap(), 0, str );
390     INT32 ret = ExtTextOut32A( hdc, x, y, flags, lprect, p, count, lpDx );
391     HeapFree( GetProcessHeap(), 0, p );
392     return ret;
393 }
394
395
396 /***********************************************************************
397  *           TextOut16    (GDI.33)
398  */
399 BOOL16 WINAPI TextOut16( HDC16 hdc, INT16 x, INT16 y, LPCSTR str, INT16 count )
400 {
401     return ExtTextOut16( hdc, x, y, 0, NULL, str, count, NULL );
402 }
403
404
405 /***********************************************************************
406  *           TextOut32A    (GDI32.355)
407  */
408 BOOL32 WINAPI TextOut32A( HDC32 hdc, INT32 x, INT32 y, LPCSTR str, INT32 count )
409 {
410     return ExtTextOut32A( hdc, x, y, 0, NULL, str, count, NULL );
411 }
412
413
414 /***********************************************************************
415  *           TextOut32W    (GDI32.356)
416  */
417 BOOL32 WINAPI TextOut32W(HDC32 hdc, INT32 x, INT32 y, LPCWSTR str, INT32 count)
418 {
419     return ExtTextOut32W( hdc, x, y, 0, NULL, str, count, NULL );
420 }
421
422
423 /***********************************************************************
424  *           TEXT_GrayString
425  *
426  * FIXME: The call to 16-bit code only works because the wine GDI is a 16-bit
427  * heap and we can guarantee that the handles fit in an INT16. We have to
428  * rethink the strategy once the migration to NT handles is complete.
429  * We are going to get a lot of code-duplication once this migration is
430  * completed...
431  * 
432  */
433 static BOOL32 TEXT_GrayString(HDC32 hdc, HBRUSH32 hb, 
434                               GRAYSTRINGPROC32 fn, LPARAM lp, INT32 len,
435                               INT32 x, INT32 y, INT32 cx, INT32 cy, 
436                               BOOL32 unicode, BOOL32 _32bit)
437 {
438     HBITMAP32 hbm, hbmsave;
439     HBRUSH32 hbsave;
440     HFONT32 hfsave;
441     HDC32 memdc = CreateCompatibleDC32(hdc);
442     int slen = len;
443     BOOL32 retval = TRUE;
444     RECT32 r;
445     COLORREF fg, bg;
446
447     if(!hdc) return FALSE;
448     
449     if(len == 0)
450     {
451         if(unicode)
452             slen = lstrlen32W((LPCWSTR)lp);
453         else if(_32bit)
454             slen = lstrlen32A((LPCSTR)lp);
455         else
456             slen = lstrlen32A((LPCSTR)PTR_SEG_TO_LIN(lp));
457     }
458
459     if((cx == 0 || cy == 0) && slen != -1)
460     {
461         SIZE32 s;
462         if(unicode)
463             GetTextExtentPoint32W(hdc, (LPCWSTR)lp, slen, &s);
464         else if(_32bit)
465             GetTextExtentPoint32A(hdc, (LPCSTR)lp, slen, &s);
466         else
467             GetTextExtentPoint32A(hdc, (LPCSTR)PTR_SEG_TO_LIN(lp), slen, &s);
468         if(cx == 0) cx = s.cx;
469         if(cy == 0) cy = s.cy;
470     }
471
472     r.left = r.top = 0;
473     r.right = cx;
474     r.bottom = cy;
475
476     hbm = CreateBitmap32(cx, cy, 1, 1, NULL);
477     hbmsave = (HBITMAP32)SelectObject32(memdc, hbm);
478     FillRect32(memdc, &r, (HBRUSH32)GetStockObject32(BLACK_BRUSH));
479     SetTextColor32(memdc, RGB(255, 255, 255));
480     SetBkColor32(memdc, RGB(0, 0, 0));
481     hfsave = (HFONT32)SelectObject32(memdc, GetCurrentObject(hdc, OBJ_FONT));
482             
483     if(fn)
484         if(_32bit)
485             retval = fn(memdc, lp, slen);
486         else
487             retval = (BOOL32)((BOOL16)((GRAYSTRINGPROC16)fn)((HDC16)memdc, lp, (INT16)slen));
488     else
489         if(unicode)
490             TextOut32W(memdc, 0, 0, (LPCWSTR)lp, slen);
491         else if(_32bit)
492             TextOut32A(memdc, 0, 0, (LPCSTR)lp, slen);
493         else
494             TextOut32A(memdc, 0, 0, (LPCSTR)PTR_SEG_TO_LIN(lp), slen);
495
496     SelectObject32(memdc, hfsave);
497
498 /*
499  * Windows doc says that the bitmap isn't grayed when len == -1 and
500  * the callback function returns FALSE. However, testing this on
501  * win95 showed otherwise...
502 */
503 #ifdef GRAYSTRING_USING_DOCUMENTED_BEHAVIOUR
504     if(retval || len != -1)
505 #endif
506     {
507         hbsave = (HBRUSH32)SelectObject32(memdc, CACHE_GetPattern55AABrush());
508         PatBlt32(memdc, 0, 0, cx, cy, 0x000A0329);
509         SelectObject32(memdc, hbsave);
510     }
511
512     if(hb) hbsave = (HBRUSH32)SelectObject32(hdc, hb);
513     fg = SetTextColor32(hdc, RGB(0, 0, 0));
514     bg = SetBkColor32(hdc, RGB(255, 255, 255));
515     BitBlt32(hdc, x, y, cx, cy, memdc, 0, 0, 0x00E20746);
516     SetTextColor32(hdc, fg);
517     SetBkColor32(hdc, bg);
518     if(hb) SelectObject32(hdc, hbsave);
519
520     SelectObject32(memdc, hbmsave);
521     DeleteObject32(hbm);
522     DeleteDC32(memdc);
523     return retval;
524 }
525
526
527 /***********************************************************************
528  *           GrayString16   (USER.185)
529  */
530 BOOL16 WINAPI GrayString16( HDC16 hdc, HBRUSH16 hbr, GRAYSTRINGPROC16 gsprc,
531                             LPARAM lParam, INT16 cch, INT16 x, INT16 y,
532                             INT16 cx, INT16 cy )
533 {
534     return TEXT_GrayString(hdc, hbr, (GRAYSTRINGPROC32)gsprc, lParam, cch, x, y, cx, cy, FALSE, FALSE);
535 }
536
537
538 /***********************************************************************
539  *           GrayString32A   (USER32.314)
540  */
541 BOOL32 WINAPI GrayString32A( HDC32 hdc, HBRUSH32 hbr, GRAYSTRINGPROC32 gsprc,
542                              LPARAM lParam, INT32 cch, INT32 x, INT32 y,
543                              INT32 cx, INT32 cy )
544 {
545     return TEXT_GrayString(hdc, hbr, gsprc, lParam, cch, x, y, cx, cy, FALSE, TRUE);
546 }
547
548
549 /***********************************************************************
550  *           GrayString32W   (USER32.315)
551  */
552 BOOL32 WINAPI GrayString32W( HDC32 hdc, HBRUSH32 hbr, GRAYSTRINGPROC32 gsprc,
553                              LPARAM lParam, INT32 cch, INT32 x, INT32 y,
554                              INT32 cx, INT32 cy )
555 {
556     return TEXT_GrayString(hdc, hbr, gsprc, lParam, cch, x, y, cx, cy, TRUE, TRUE);
557 }
558
559
560 /***********************************************************************
561  *           TEXT_TabbedTextOut
562  *
563  * Helper function for TabbedTextOut() and GetTabbedTextExtent().
564  * Note: this doesn't work too well for text-alignment modes other
565  *       than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
566  */
567 LONG TEXT_TabbedTextOut( HDC32 hdc, INT32 x, INT32 y, LPCSTR lpstr,
568                          INT32 count, INT32 cTabStops, const INT16 *lpTabPos16,
569                          const INT32 *lpTabPos32, INT32 nTabOrg,
570                          BOOL32 fDisplayText )
571 {
572     INT32 defWidth;
573     DWORD extent = 0;
574     int i, tabPos = x;
575     int start = x;
576
577     if (cTabStops == 1)
578     {
579         defWidth = lpTabPos32 ? *lpTabPos32 : *lpTabPos16;
580         cTabStops = 0;
581     }
582     else
583     {
584         TEXTMETRIC16 tm;
585         GetTextMetrics16( hdc, &tm );
586         defWidth = 8 * tm.tmAveCharWidth;
587     }
588     
589     while (count > 0)
590     {
591         for (i = 0; i < count; i++)
592             if (lpstr[i] == '\t') break;
593         extent = GetTextExtent( hdc, lpstr, i );
594         if (lpTabPos32)
595         {
596             while ((cTabStops > 0) &&
597                    (nTabOrg + *lpTabPos32 <= x + LOWORD(extent)))
598             {
599                 lpTabPos32++;
600                 cTabStops--;
601             }
602         }
603         else
604         {
605             while ((cTabStops > 0) &&
606                    (nTabOrg + *lpTabPos16 <= x + LOWORD(extent)))
607             {
608                 lpTabPos16++;
609                 cTabStops--;
610             }
611         }
612         if (i == count)
613             tabPos = x + LOWORD(extent);
614         else if (cTabStops > 0)
615             tabPos = nTabOrg + (lpTabPos32 ? *lpTabPos32 : *lpTabPos16);
616         else
617             tabPos = nTabOrg + ((x + LOWORD(extent) - nTabOrg) / defWidth + 1) * defWidth;
618         if (fDisplayText)
619         {
620             RECT32 r;
621             SetRect32( &r, x, y, tabPos, y+HIWORD(extent) );
622             ExtTextOut32A( hdc, x, y,
623                            GetBkMode32(hdc) == OPAQUE ? ETO_OPAQUE : 0,
624                            &r, lpstr, i, NULL );
625         }
626         x = tabPos;
627         count -= i+1;
628         lpstr += i+1;
629     }
630     return MAKELONG(tabPos - start, HIWORD(extent));
631 }
632
633
634 /***********************************************************************
635  *           TabbedTextOut16    (USER.196)
636  */
637 LONG WINAPI TabbedTextOut16( HDC16 hdc, INT16 x, INT16 y, LPCSTR lpstr,
638                              INT16 count, INT16 cTabStops,
639                              const INT16 *lpTabPos, INT16 nTabOrg )
640 {
641     dprintf_text( stddeb, "TabbedTextOut16: %04x %d,%d '%.*s' %d\n",
642                   hdc, x, y, count, lpstr, count );
643     return TEXT_TabbedTextOut( hdc, x, y, lpstr, count, cTabStops,
644                                lpTabPos, NULL, nTabOrg, TRUE );
645 }
646
647
648 /***********************************************************************
649  *           TabbedTextOut32A    (USER32.541)
650  */
651 LONG WINAPI TabbedTextOut32A( HDC32 hdc, INT32 x, INT32 y, LPCSTR lpstr,
652                               INT32 count, INT32 cTabStops,
653                               const INT32 *lpTabPos, INT32 nTabOrg )
654 {
655     dprintf_text( stddeb, "TabbedTextOut32A: %04x %d,%d '%.*s' %d\n",
656                   hdc, x, y, count, lpstr, count );
657     return TEXT_TabbedTextOut( hdc, x, y, lpstr, count, cTabStops,
658                                NULL, lpTabPos, nTabOrg, TRUE );
659 }
660
661
662 /***********************************************************************
663  *           TabbedTextOut32W    (USER32.542)
664  */
665 LONG WINAPI TabbedTextOut32W( HDC32 hdc, INT32 x, INT32 y, LPCWSTR str,
666                               INT32 count, INT32 cTabStops,
667                               const INT32 *lpTabPos, INT32 nTabOrg )
668 {
669     LONG ret;
670     LPSTR p = HEAP_xalloc( GetProcessHeap(), 0, count + 1 );
671     lstrcpynWtoA( p, str, count + 1 );
672     ret = TabbedTextOut32A( hdc, x, y, p, count, cTabStops,
673                             lpTabPos, nTabOrg );
674     HeapFree( GetProcessHeap(), 0, p );
675     return ret;
676 }
677
678
679 /***********************************************************************
680  *           GetTabbedTextExtent16    (USER.197)
681  */
682 DWORD WINAPI GetTabbedTextExtent16( HDC16 hdc, LPCSTR lpstr, INT16 count, 
683                                     INT16 cTabStops, const INT16 *lpTabPos )
684 {
685     dprintf_text( stddeb, "GetTabbedTextExtent: %04x '%.*s' %d\n",
686                   hdc, count, lpstr, count );
687     return TEXT_TabbedTextOut( hdc, 0, 0, lpstr, count, cTabStops,
688                                lpTabPos, NULL, 0, FALSE );
689 }
690
691
692 /***********************************************************************
693  *           GetTabbedTextExtent32A    (USER32.292)
694  */
695 DWORD WINAPI GetTabbedTextExtent32A( HDC32 hdc, LPCSTR lpstr, INT32 count, 
696                                      INT32 cTabStops, const INT32 *lpTabPos )
697 {
698     dprintf_text( stddeb, "GetTabbedTextExtent: %04x '%.*s' %d\n",
699                   hdc, count, lpstr, count );
700     return TEXT_TabbedTextOut( hdc, 0, 0, lpstr, count, cTabStops,
701                                NULL, lpTabPos, 0, FALSE );
702 }
703
704
705 /***********************************************************************
706  *           GetTabbedTextExtent32W    (USER32.293)
707  */
708 DWORD WINAPI GetTabbedTextExtent32W( HDC32 hdc, LPCWSTR lpstr, INT32 count, 
709                                      INT32 cTabStops, const INT32 *lpTabPos )
710 {
711     LONG ret;
712     LPSTR p = HEAP_xalloc( GetProcessHeap(), 0, count + 1 );
713     lstrcpynWtoA( p, lpstr, count + 1 );
714     ret = GetTabbedTextExtent32A( hdc, p, count, cTabStops, lpTabPos );
715     HeapFree( GetProcessHeap(), 0, p );
716     return ret;
717 }
718
719 /***********************************************************************
720  *           GetTextCharset    (GDI32.226) (GDI.612)
721  */
722 INT32 WINAPI GetTextCharset32(HDC32 hdc)
723 {
724     fprintf(stdnimp,"GetTextCharset(0x%x)\n",hdc);
725     return DEFAULT_CHARSET; /* FIXME */
726 }
727
728 INT16 WINAPI GetTextCharset16(HDC16 hdc)
729 {
730     return GetTextCharset32(hdc);
731 }
732
733 /***********************************************************************
734  *           GetTextCharsetInfo    (GDI32.381)
735  */
736 INT32 WINAPI GetTextCharsetInfo(HDC32 hdc,LPCHARSETINFO csi,DWORD flags)
737 {
738     fprintf(stdnimp,"GetTextCharsetInfo(0x%x,%p,%08lx), stub!\n",hdc,csi,flags);
739     if (csi) {
740         csi->ciCharset = DEFAULT_CHARSET;
741         csi->ciACP = GetACP();
742     }
743     /* ... fill fontstruct too ... */
744     return DEFAULT_CHARSET;
745 }