Release 961222
[wine] / objects / text.c
1 /*
2  * text functions
3  *
4  * Copyright 1993, 1994 Alexandre Julliard
5  *
6  */
7
8 #include <stdlib.h>
9 #include <X11/Xatom.h>
10 #include "windows.h"
11 #include "dc.h"
12 #include "gdi.h"
13 #include "callback.h"
14 #include "heap.h"
15 #include "metafile.h"
16 #include "stddebug.h"
17 /* #define DEBUG_TEXT */
18 #include "debug.h"
19 #include "xmalloc.h"
20
21 #define TAB     9
22 #define LF     10
23 #define CR     13
24 #define SPACE  32
25 #define PREFIX 38
26
27 #define SWAP_INT(a,b)  { int t = a; a = b; b = t; }
28
29 static int tabstop = 8;
30 static int tabwidth;
31 static int spacewidth;
32 static int prefix_offset;
33
34 extern int CLIPPING_IntersectClipRect( DC * dc, short left, short top,
35                                          short right, short bottom, UINT16 flags);
36
37 static const char *TEXT_NextLine( HDC16 hdc, const char *str, int *count,
38                                   char *dest, int *len, int width, WORD format)
39 {
40     /* Return next line of text from a string.
41      * 
42      * hdc - handle to DC.
43      * str - string to parse into lines.
44      * count - length of str.
45      * dest - destination in which to return line.
46      * len - length of resultant line in dest in chars.
47      * width - maximum width of line in pixels.
48      * format - format type passed to DrawText.
49      *
50      * Returns pointer to next char in str after end of the line
51      * or NULL if end of str reached.
52      */
53
54     int i = 0, j = 0, k;
55     int plen = 0;
56     int numspaces;
57     SIZE16 size;
58     int lasttab = 0;
59     int wb_i = 0, wb_j = 0, wb_count = 0;
60
61     while (*count)
62     {
63         switch (str[i])
64         {
65         case CR:
66         case LF:
67             if (!(format & DT_SINGLELINE))
68             {
69                 if (str[i] == CR && str[i+1] == LF)
70                     i++;
71                 i++;
72                 *len = j;
73                 (*count)--;
74                 return (&str[i]);
75             }
76             dest[j++] = str[i++];
77             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
78                 (format & DT_WORDBREAK))
79             {
80                 if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
81                     return NULL;
82                 plen += size.cx;
83             }
84             break;
85             
86         case PREFIX:
87             if (!(format & DT_NOPREFIX))
88             {
89                 if (str[++i] != PREFIX)
90                 {
91                     prefix_offset = j;
92                     break;
93                 }
94             }
95             dest[j++] = str[i++];
96             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
97                 (format & DT_WORDBREAK))
98             {
99                 if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
100                     return NULL;
101                 plen += size.cx;
102             }
103             break;
104             
105         case TAB:
106             if (format & DT_EXPANDTABS)
107             {
108                 wb_i = ++i;
109                 wb_j = j;
110                 wb_count = *count;
111
112                 if (!GetTextExtentPoint16(hdc, &dest[lasttab], j - lasttab,
113                                                                  &size))
114                     return NULL;
115
116                 numspaces = (tabwidth - size.cx) / spacewidth;
117                 for (k = 0; k < numspaces; k++)
118                     dest[j++] = SPACE;
119                 plen += tabwidth - size.cx;
120                 lasttab = wb_j + numspaces;
121             }
122             else
123             {
124                 dest[j++] = str[i++];
125                 if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
126                     (format & DT_WORDBREAK))
127                 {
128                     if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
129                         return NULL;
130                     plen += size.cx;
131                 }
132             }
133             break;
134
135         case SPACE:
136             dest[j++] = str[i++];
137             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
138                 (format & DT_WORDBREAK))
139             {
140                 wb_i = i;
141                 wb_j = j - 1;
142                 wb_count = *count;
143                 if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
144                     return NULL;
145                 plen += size.cx;
146             }
147             break;
148
149         default:
150             dest[j++] = str[i++];
151             if (!(format & DT_NOCLIP) || !(format & DT_NOPREFIX) ||
152                 (format & DT_WORDBREAK))
153             {
154                 if (!GetTextExtentPoint16(hdc, &dest[j-1], 1, &size))
155                     return NULL;
156                 plen += size.cx;
157             }
158         }
159
160         (*count)--;
161         if (!(format & DT_NOCLIP) || (format & DT_WORDBREAK))
162         {
163             if (plen > width)
164             {
165                 if (format & DT_WORDBREAK)
166                 {
167                     if (wb_j)
168                     {
169                         *len = wb_j;
170                         *count = wb_count - 1;
171                         return (&str[wb_i]);
172                     }
173                 }
174                 else
175                 {
176                     *len = j;
177                     return (&str[i]);
178                 }
179             }
180         }
181     }
182     
183     *len = j;
184     return NULL;
185 }
186
187
188 /***********************************************************************
189  *           DrawText16    (USER.85)
190  */
191 INT16 DrawText16( HDC16 hdc, LPCSTR str, INT16 i_count,
192                   LPRECT16 rect, UINT16 flags )
193 {
194     SIZE16 size;
195     const char *strPtr;
196     static char line[1024];
197     int len, lh, count=i_count;
198     int prefix_x = 0;
199     int prefix_end = 0;
200     TEXTMETRIC16 tm;
201     int x = rect->left, y = rect->top;
202     int width = rect->right - rect->left;
203     int max_width = 0;
204
205     dprintf_text(stddeb,"DrawText: '%s', %d , [(%d,%d),(%d,%d)]\n", str,
206                  count, rect->left, rect->top, rect->right, rect->bottom);
207     
208     if (count == -1) count = strlen(str);
209     strPtr = str;
210
211     GetTextMetrics16(hdc, &tm);
212     if (flags & DT_EXTERNALLEADING)
213         lh = tm.tmHeight + tm.tmExternalLeading;
214     else
215         lh = tm.tmHeight;
216
217     if (flags & DT_TABSTOP)
218         tabstop = flags >> 8;
219
220     if (flags & DT_EXPANDTABS)
221     {
222         GetTextExtentPoint16(hdc, " ", 1, &size);
223         spacewidth = size.cx;
224         GetTextExtentPoint16(hdc, "o", 1, &size);
225         tabwidth = size.cx * tabstop;
226     }
227
228     if (flags & DT_CALCRECT) flags |= DT_NOCLIP;
229
230     do
231     {
232         prefix_offset = -1;
233         strPtr = TEXT_NextLine(hdc, strPtr, &count, line, &len, width, flags);
234
235         if (prefix_offset != -1)
236         {
237             GetTextExtentPoint16(hdc, line, prefix_offset, &size);
238             prefix_x = size.cx;
239             GetTextExtentPoint16(hdc, line, prefix_offset + 1, &size);
240             prefix_end = size.cx - 1;
241         }
242
243         if (!GetTextExtentPoint16(hdc, line, len, &size)) return 0;
244         if (flags & DT_CENTER) x = (rect->left + rect->right -
245                                     size.cx) / 2;
246         else if (flags & DT_RIGHT) x = rect->right - size.cx;
247
248         if (flags & DT_SINGLELINE)
249         {
250             if (flags & DT_VCENTER) y = rect->top + 
251                 (rect->bottom - rect->top) / 2 - size.cy / 2;
252             else if (flags & DT_BOTTOM) y = rect->bottom - size.cy;
253         }
254         if (!(flags & DT_CALCRECT))
255         {
256             if (!ExtTextOut16(hdc, x, y, (flags & DT_NOCLIP) ? 0 : ETO_CLIPPED,
257                               rect, line, len, NULL )) return 0;
258             if (prefix_offset != -1)
259             {
260                 HPEN32 hpen = CreatePen32( PS_SOLID, 1, GetTextColor32(hdc) );
261                 HPEN32 oldPen = SelectObject32( hdc, hpen );
262                 MoveTo(hdc, x + prefix_x, y + tm.tmAscent + 1 );
263                 LineTo32(hdc, x + prefix_end, y + tm.tmAscent + 1 );
264                 SelectObject32( hdc, oldPen );
265                 DeleteObject32( hpen );
266             }
267         }
268         else if (size.cx > max_width)
269             max_width = size.cx;
270
271         y += lh;
272         if (strPtr)
273         {
274             if (!(flags & DT_NOCLIP))
275             {
276                 if (y > rect->bottom - lh)
277                     break;
278             }
279         }
280     }
281     while (strPtr);
282     if (flags & DT_CALCRECT)
283     {
284         rect->right = rect->left + max_width;
285         rect->bottom = y;
286     }
287     return y - rect->top;
288 }
289
290
291 /***********************************************************************
292  *           DrawText32A    (USER32.163)
293  */
294 INT32 DrawText32A( HDC32 hdc, LPCSTR str, INT32 count,
295                    LPRECT32 rect, UINT32 flags )
296 {
297     RECT16 rect16;
298     INT16 ret;
299
300     if (!rect)
301         return DrawText16( (HDC16)hdc, str, (INT16)count, NULL, (UINT16)flags);
302     CONV_RECT32TO16( rect, &rect16 );
303     ret = DrawText16( (HDC16)hdc, str, (INT16)count, &rect16, (UINT16)flags );
304     CONV_RECT16TO32( &rect16, rect );
305     return ret;
306 }
307
308
309 /***********************************************************************
310  *           DrawText32W    (USER32.166)
311  */
312 INT32 DrawText32W( HDC32 hdc, LPCWSTR str, INT32 count,
313                    LPRECT32 rect, UINT32 flags )
314 {
315     LPSTR p = HEAP_strdupWtoA( GetProcessHeap(), 0, str );
316     INT32 ret = DrawText32A( hdc, p, count, rect, flags );
317     HeapFree( GetProcessHeap(), 0, p );
318     return ret;
319 }
320
321
322 /***********************************************************************
323  *           ExtTextOut16    (GDI.351)
324  */
325 BOOL16 ExtTextOut16( HDC16 hdc, INT16 x, INT16 y, UINT16 flags,
326                      const RECT16 *lprect, LPCSTR str, UINT16 count,
327                      const INT16 *lpDx )
328 {
329     HRGN32      hRgnClip = 0;
330     int         dir, ascent, descent, i;
331     XCharStruct info;
332     XFontStruct *font;
333     RECT16      rect;
334
335     DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
336     if (!dc) 
337     {
338         dc = (DC *)GDI_GetObjPtr( hdc, METAFILE_DC_MAGIC );
339         if (!dc) return FALSE;
340         MF_ExtTextOut( dc, x, y, flags, lprect, str, count, lpDx );
341         return TRUE;
342     }
343
344     if (!DC_SetupGCForText( dc )) return TRUE;
345     font = dc->u.x.font.fstruct;
346
347     dprintf_text(stddeb,"ExtTextOut: hdc=%04x %d,%d '%*.*s', %d  flags=%d\n",
348             hdc, x, y, count, count, str, count, flags);
349     if (lprect != NULL) dprintf_text(stddeb, "\trect=(%d,%d- %d,%d)\n",
350                                      lprect->left, lprect->top,
351                                      lprect->right, lprect->bottom );
352
353       /* Setup coordinates */
354
355     if (dc->w.textAlign & TA_UPDATECP)
356     {
357         x = dc->w.CursPosX;
358         y = dc->w.CursPosY;
359     }
360
361     if (flags & (ETO_OPAQUE | ETO_CLIPPED))  /* there's a rectangle */
362     {
363         if (!lprect)  /* not always */
364         {
365             SIZE16 sz;
366             if (flags & ETO_CLIPPED)  /* Can't clip with no rectangle */
367               return FALSE;
368             if (!GetTextExtentPoint16( hdc, str, count, &sz ))
369               return FALSE;
370             rect.left   = XLPTODP( dc, x );
371             rect.right  = XLPTODP( dc, x+sz.cx );
372             rect.top    = YLPTODP( dc, y );
373             rect.bottom = YLPTODP( dc, y+sz.cy );
374         }
375         else
376         {
377             rect.left   = XLPTODP( dc, lprect->left );
378             rect.right  = XLPTODP( dc, lprect->right );
379             rect.top    = YLPTODP( dc, lprect->top );
380             rect.bottom = YLPTODP( dc, lprect->bottom );
381         }
382         if (rect.right < rect.left) SWAP_INT( rect.left, rect.right );
383         if (rect.bottom < rect.top) SWAP_INT( rect.top, rect.bottom );
384     }
385
386     x = XLPTODP( dc, x );
387     y = YLPTODP( dc, y );
388
389     dprintf_text(stddeb,"\treal coord: x=%i, y=%i, rect=(%d,%d-%d,%d)\n",
390                           x, y, rect.left, rect.top, rect.right, rect.bottom);
391
392       /* Draw the rectangle */
393
394     if (flags & ETO_OPAQUE)
395     {
396         XSetForeground( display, dc->u.x.gc, dc->w.backgroundPixel );
397         XFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
398                         dc->w.DCOrgX + rect.left, dc->w.DCOrgY + rect.top,
399                         rect.right-rect.left, rect.bottom-rect.top );
400     }
401     if (!count) return TRUE;  /* Nothing more to do */
402
403       /* Compute text starting position */
404
405     XTextExtents( font, str, count, &dir, &ascent, &descent, &info );
406
407     if (lpDx) /* have explicit character cell x offsets */
408     {
409         /* sum lpDx array and add the width of last character */
410
411         info.width = XTextWidth( font, str + count - 1, 1) + dc->w.charExtra;
412         if (str[count-1] == (char)dc->u.x.font.metrics.tmBreakChar)
413             info.width += dc->w.breakExtra;
414
415         for (i = 0; i < count; i++) info.width += lpDx[i];
416     }
417     else
418        info.width += count*dc->w.charExtra + dc->w.breakExtra*dc->w.breakCount;
419
420     switch( dc->w.textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER) )
421     {
422       case TA_LEFT:
423           if (dc->w.textAlign & TA_UPDATECP)
424               dc->w.CursPosX = XDPTOLP( dc, x + info.width );
425           break;
426       case TA_RIGHT:
427           x -= info.width;
428           if (dc->w.textAlign & TA_UPDATECP) dc->w.CursPosX = XDPTOLP( dc, x );
429           break;
430       case TA_CENTER:
431           x -= info.width / 2;
432           break;
433     }
434
435     switch( dc->w.textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE) )
436     {
437       case TA_TOP:
438           y += font->ascent;
439           break;
440       case TA_BOTTOM:
441           y -= font->descent;
442           break;
443       case TA_BASELINE:
444           break;
445     }
446
447       /* Set the clip region */
448
449     if (flags & ETO_CLIPPED)
450     {
451         hRgnClip = dc->w.hClipRgn;
452         CLIPPING_IntersectClipRect( dc, rect.left, rect.top, rect.right,
453                                     rect.bottom, CLIP_INTERSECT|CLIP_KEEPRGN );
454     }
455
456       /* Draw the text background if necessary */
457
458     if (dc->w.backgroundMode != TRANSPARENT)
459     {
460           /* If rectangle is opaque and clipped, do nothing */
461         if (!(flags & ETO_CLIPPED) || !(flags & ETO_OPAQUE))
462         {
463               /* Only draw if rectangle is not opaque or if some */
464               /* text is outside the rectangle */
465             if (!(flags & ETO_OPAQUE) ||
466                 (x < rect.left) ||
467                 (x + info.width >= rect.right) ||
468                 (y-font->ascent < rect.top) ||
469                 (y+font->descent >= rect.bottom))
470             {
471                 XSetForeground( display, dc->u.x.gc, dc->w.backgroundPixel );
472                 XFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
473                                 dc->w.DCOrgX + x,
474                                 dc->w.DCOrgY + y - font->ascent,
475                                 info.width,
476                                 font->ascent + font->descent );
477             }
478         }
479     }
480     
481     /* Draw the text (count > 0 verified) */
482
483     XSetForeground( display, dc->u.x.gc, dc->w.textPixel );
484     if (!dc->w.charExtra && !dc->w.breakExtra && !lpDx)
485     {
486         XDrawString( display, dc->u.x.drawable, dc->u.x.gc, 
487                      dc->w.DCOrgX + x, dc->w.DCOrgY + y, str, count );
488     }
489     else  /* Now the fun begins... */
490     {
491         XTextItem *items, *pitem;
492         int delta;
493
494         /* allocate max items */
495
496         pitem = items = xmalloc( count * sizeof(XTextItem) );
497         delta = i = 0;
498         while (i < count)
499         {
500             /* initialize text item with accumulated delta */
501
502             pitem->chars  = (char *)str + i;
503             pitem->delta  = delta; 
504             pitem->nchars = 0;
505             pitem->font   = None;
506             delta = 0;
507
508             /* stuff characters into the same XTextItem until new delta 
509              * becomes  non-zero */
510
511             do
512             {
513                 if (lpDx) delta += lpDx[i] - XTextWidth( font, str + i, 1);
514                 else
515                 {
516                     delta += dc->w.charExtra;
517                     if (str[i] == (char)dc->u.x.font.metrics.tmBreakChar)
518                         delta += dc->w.breakExtra;
519                 }
520                 pitem->nchars++;
521             } 
522             while ((++i < count) && !delta);
523             pitem++;
524         }
525
526         XDrawText( display, dc->u.x.drawable, dc->u.x.gc,
527                    dc->w.DCOrgX + x, dc->w.DCOrgY + y, items, pitem - items );
528         free( items );
529     }
530
531       /* Draw underline and strike-out if needed */
532
533     if (dc->u.x.font.metrics.tmUnderlined)
534     {
535         long linePos, lineWidth;       
536         if (!XGetFontProperty( font, XA_UNDERLINE_POSITION, &linePos ))
537             linePos = font->descent-1;
538         if (!XGetFontProperty( font, XA_UNDERLINE_THICKNESS, &lineWidth ))
539             lineWidth = 0;
540         else if (lineWidth == 1) lineWidth = 0;
541         XSetLineAttributes( display, dc->u.x.gc, lineWidth,
542                             LineSolid, CapRound, JoinBevel ); 
543         XDrawLine( display, dc->u.x.drawable, dc->u.x.gc,
544                    dc->w.DCOrgX + x, dc->w.DCOrgY + y + linePos,
545                    dc->w.DCOrgX + x + info.width, dc->w.DCOrgY + y + linePos );
546     }
547     if (dc->u.x.font.metrics.tmStruckOut)
548     {
549         long lineAscent, lineDescent;
550         if (!XGetFontProperty( font, XA_STRIKEOUT_ASCENT, &lineAscent ))
551             lineAscent = font->ascent / 3;
552         if (!XGetFontProperty( font, XA_STRIKEOUT_DESCENT, &lineDescent ))
553             lineDescent = -lineAscent;
554         XSetLineAttributes( display, dc->u.x.gc, lineAscent + lineDescent,
555                             LineSolid, CapRound, JoinBevel ); 
556         XDrawLine( display, dc->u.x.drawable, dc->u.x.gc,
557                    dc->w.DCOrgX + x, dc->w.DCOrgY + y - lineAscent,
558                    dc->w.DCOrgX + x + info.width, dc->w.DCOrgY + y - lineAscent );
559     }
560
561     if (flags & ETO_CLIPPED) SelectClipRgn32( hdc, hRgnClip );
562     return TRUE;
563 }
564
565
566 /***********************************************************************
567  *           ExtTextOut32A    (GDI32.98)
568  */
569 BOOL32 ExtTextOut32A( HDC32 hdc, INT32 x, INT32 y, UINT32 flags,
570                       const RECT32 *lprect, LPCSTR str, UINT32 count,
571                       const INT32 *lpDx )
572 {
573     RECT16 rect16;
574
575     if (lpDx) fprintf( stderr, "ExtTextOut32A: lpDx not implemented\n" );
576     if (!lprect)
577         return ExtTextOut16( (HDC16)hdc, (INT16)x, (INT16)y, (UINT16)flags,
578                              NULL, str, (UINT16)count, NULL );
579     CONV_RECT32TO16( lprect, &rect16 );
580     return ExtTextOut16( (HDC16)hdc, (INT16)x, (INT16)y, (UINT16)flags,
581                          &rect16, str, (UINT16)count, NULL );
582 }
583
584
585 /***********************************************************************
586  *           ExtTextOut32W    (GDI32.99)
587  */
588 BOOL32 ExtTextOut32W( HDC32 hdc, INT32 x, INT32 y, UINT32 flags,
589                       const RECT32 *lprect, LPCWSTR str, UINT32 count,
590                       const INT32 *lpDx )
591 {
592     LPSTR p = HEAP_strdupWtoA( GetProcessHeap(), 0, str );
593     INT32 ret = ExtTextOut32A( hdc, x, y, flags, lprect, p, count, lpDx );
594     HeapFree( GetProcessHeap(), 0, p );
595     return ret;
596 }
597
598
599 /***********************************************************************
600  *           TextOut16    (GDI.33)
601  */
602 BOOL16 TextOut16( HDC16 hdc, INT16 x, INT16 y, LPCSTR str, INT16 count )
603 {
604     return ExtTextOut16( hdc, x, y, 0, NULL, str, count, NULL );
605 }
606
607
608 /***********************************************************************
609  *           TextOut32A    (GDI32.355)
610  */
611 BOOL32 TextOut32A( HDC32 hdc, INT32 x, INT32 y, LPCSTR str, INT32 count )
612 {
613     return ExtTextOut32A( hdc, x, y, 0, NULL, str, count, NULL );
614 }
615
616
617 /***********************************************************************
618  *           TextOut32W    (GDI32.356)
619  */
620 BOOL32 TextOut32W( HDC32 hdc, INT32 x, INT32 y, LPCWSTR str, INT32 count )
621 {
622     return ExtTextOut32W( hdc, x, y, 0, NULL, str, count, NULL );
623 }
624
625
626 /***********************************************************************
627  *           GrayString   (USER.185)
628  */
629 BOOL GrayString(HDC16 hdc, HBRUSH16 hbr, GRAYSTRINGPROC16 gsprc, LPARAM lParam, 
630                 INT cch, INT x, INT y, INT cx, INT cy)
631 {
632     BOOL ret;
633     COLORREF current_color;
634
635     if (!cch) cch = lstrlen16( (LPCSTR)PTR_SEG_TO_LIN(lParam) );
636     if (gsprc) return gsprc( hdc, lParam, cch );
637     current_color = GetTextColor32( hdc );
638     SetTextColor( hdc, GetSysColor(COLOR_GRAYTEXT) );
639     ret = TextOut16( hdc, x, y, (LPCSTR)PTR_SEG_TO_LIN(lParam), cch );
640     SetTextColor( hdc, current_color );
641     return ret;
642 }
643
644
645 /***********************************************************************
646  *           TEXT_TabbedTextOut
647  *
648  * Helper function for TabbedTextOut() and GetTabbedTextExtent().
649  * Note: this doesn't work too well for text-alignment modes other
650  *       than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
651  */
652 LONG TEXT_TabbedTextOut( HDC16 hdc, int x, int y, LPSTR lpstr, int count, 
653                          int cTabStops, LPINT16 lpTabPos, int nTabOrg,
654                          BOOL fDisplayText)
655 {
656     WORD defWidth;
657     DWORD extent = 0;
658     int i, tabPos = x;
659     int start = x;
660
661     if (cTabStops == 1)
662     {
663         defWidth = *lpTabPos;
664         cTabStops = 0;
665     }
666     else
667     {
668         TEXTMETRIC16 tm;
669         GetTextMetrics16( hdc, &tm );
670         defWidth = 8 * tm.tmAveCharWidth;
671     }
672     
673     while (count > 0)
674     {
675         for (i = 0; i < count; i++)
676             if (lpstr[i] == '\t') break;
677         extent = GetTextExtent( hdc, lpstr, i );
678         while ((cTabStops > 0) && (nTabOrg + *lpTabPos <= x + LOWORD(extent)))
679         {
680             lpTabPos++;
681             cTabStops--;
682         }
683         if (i == count)
684             tabPos = x + LOWORD(extent);
685         else if (cTabStops > 0)
686             tabPos = nTabOrg + *lpTabPos;
687         else
688             tabPos = nTabOrg + ((x + LOWORD(extent) - nTabOrg) / defWidth + 1) * defWidth;
689         if (fDisplayText)
690         {
691             RECT16 r;
692             SetRect16( &r, x, y, tabPos, y+HIWORD(extent) );
693             ExtTextOut16( hdc, x, y,
694                           GetBkMode32(hdc) == OPAQUE ? ETO_OPAQUE : 0,
695                           &r, lpstr, i, NULL );
696         }
697         x = tabPos;
698         count -= i+1;
699         lpstr += i+1;
700     }
701     return MAKELONG(tabPos - start, HIWORD(extent));
702 }
703
704
705 /***********************************************************************
706  *           TabbedTextOut    (USER.196)
707  */
708 LONG TabbedTextOut( HDC16 hdc, short x, short y, LPSTR lpstr, short count, 
709                     short cTabStops, LPINT16 lpTabPos, short nTabOrg )
710 {
711     dprintf_text( stddeb, "TabbedTextOut: %04x %d,%d '%*.*s' %d\n",
712                   hdc, x, y, count, count, lpstr, count );
713     return TEXT_TabbedTextOut( hdc, x, y, lpstr, count, cTabStops,
714                                lpTabPos, nTabOrg, TRUE );
715 }
716
717
718 /***********************************************************************
719  *           GetTabbedTextExtent    (USER.197)
720  */
721 DWORD GetTabbedTextExtent( HDC16 hdc, LPSTR lpstr, int count, 
722                           int cTabStops, LPINT16 lpTabPos )
723 {
724     dprintf_text( stddeb, "GetTabbedTextExtent: %04x '%*.*s' %d\n",
725                   hdc, count, count, lpstr, count );
726     return TEXT_TabbedTextOut( hdc, 0, 0, lpstr, count, cTabStops,
727                                lpTabPos, 0, FALSE );
728 }