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