Release 970824
[wine] / graphics / x11drv / text.c
1 /*
2  * X11 graphics driver text functions
3  *
4  * Copyright 1993,1994 Alexandre Julliard
5  */
6
7 #include <stdlib.h>
8 #include <X11/Xlib.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 "x11font.h"
16 #include "stddebug.h"
17 /* #define DEBUG_TEXT */
18 #include "debug.h"
19 #include "xmalloc.h"
20
21 #define SWAP_INT(a,b)  { int t = a; a = b; b = t; }
22
23
24 extern int CLIPPING_IntersectClipRect( DC * dc, short left, short top,
25                                        short right, short bottom, UINT16 flags);
26
27 /***********************************************************************
28  *           X11DRV_ExtTextOut
29  */
30 BOOL32
31 X11DRV_ExtTextOut( DC *dc, INT32 x, INT32 y, UINT32 flags,
32                    const RECT32 *lprect, LPCSTR str, UINT32 count,
33                    const INT32 *lpDx )
34 {
35     HRGN32              hRgnClip = 0;
36     int                 dir, ascent, descent, i;
37     fontObject*         pfo;
38     XCharStruct         info;
39     XFontStruct*        font;
40     RECT32              rect;
41     char                dfBreakChar, lfUnderline, lfStrikeOut;
42
43     if (!DC_SetupGCForText( dc )) return TRUE;
44
45     pfo = XFONT_GetFontObject( dc->u.x.font );
46     font = pfo->fs;
47
48     dfBreakChar = (char)pfo->fi->df.dfBreakChar;
49     lfUnderline = (pfo->fo_flags & FO_SYNTH_UNDERLINE) ? 1 : 0;
50     lfStrikeOut = (pfo->fo_flags & FO_SYNTH_STRIKEOUT) ? 1 : 0;
51
52     dprintf_text(stddeb,"ExtTextOut: hdc=%04x df=%04x %d,%d '%.*s', %d  flags=%d\n",
53                  dc->hSelf, (UINT16)(dc->u.x.font), x, y, (int)count, str, count, flags);
54
55     if (lprect != NULL) dprintf_text(stddeb, "\trect=(%d,%d- %d,%d)\n",
56                                      lprect->left, lprect->top,
57                                      lprect->right, lprect->bottom );
58       /* Setup coordinates */
59
60     if (dc->w.textAlign & TA_UPDATECP)
61     {
62         x = dc->w.CursPosX;
63         y = dc->w.CursPosY;
64     }
65
66     if (flags & (ETO_OPAQUE | ETO_CLIPPED))  /* there's a rectangle */
67     {
68         if (!lprect)  /* not always */
69         {
70             SIZE32 sz;
71             if (flags & ETO_CLIPPED)  /* Can't clip with no rectangle */
72               return FALSE;
73             if (!X11DRV_GetTextExtentPoint( dc, str, count, &sz ))
74               return FALSE;
75             rect.left   = XLPTODP( dc, x );
76             rect.right  = XLPTODP( dc, x+sz.cx );
77             rect.top    = YLPTODP( dc, y );
78             rect.bottom = YLPTODP( dc, y+sz.cy );
79         }
80         else
81         {
82             rect.left   = XLPTODP( dc, lprect->left );
83             rect.right  = XLPTODP( dc, lprect->right );
84             rect.top    = YLPTODP( dc, lprect->top );
85             rect.bottom = YLPTODP( dc, lprect->bottom );
86         }
87         if (rect.right < rect.left) SWAP_INT( rect.left, rect.right );
88         if (rect.bottom < rect.top) SWAP_INT( rect.top, rect.bottom );
89     }
90
91     x = XLPTODP( dc, x );
92     y = YLPTODP( dc, y );
93
94     dprintf_text(stddeb,"\treal coord: x=%i, y=%i, rect=(%d,%d-%d,%d)\n",
95                           x, y, rect.left, rect.top, rect.right, rect.bottom);
96
97       /* Draw the rectangle */
98
99     if (flags & ETO_OPAQUE)
100     {
101         XSetForeground( display, dc->u.x.gc, dc->w.backgroundPixel );
102         XFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
103                         dc->w.DCOrgX + rect.left, dc->w.DCOrgY + rect.top,
104                         rect.right-rect.left, rect.bottom-rect.top );
105     }
106     if (!count) return TRUE;  /* Nothing more to do */
107
108       /* Compute text starting position */
109
110     if (lpDx) /* have explicit character cell x offsets in logical coordinates */
111     {
112         int extra = dc->wndExtX / 2;
113         for (i = info.width = 0; i < count; i++) info.width += lpDx[i];
114         info.width = (info.width * dc->vportExtX + extra ) / dc->wndExtX;
115     }
116     else
117     {
118         XTextExtents( font, str, count, &dir, &ascent, &descent, &info );
119         info.width += count*dc->w.charExtra + dc->w.breakExtra*dc->w.breakCount;
120     }
121
122     switch( dc->w.textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER) )
123     {
124       case TA_LEFT:
125           if (dc->w.textAlign & TA_UPDATECP)
126               dc->w.CursPosX = XDPTOLP( dc, x + info.width );
127           break;
128       case TA_RIGHT:
129           x -= info.width;
130           if (dc->w.textAlign & TA_UPDATECP) dc->w.CursPosX = XDPTOLP( dc, x );
131           break;
132       case TA_CENTER:
133           x -= info.width / 2;
134           break;
135     }
136
137     switch( dc->w.textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE) )
138     {
139       case TA_TOP:
140           y += font->ascent;
141           break;
142       case TA_BOTTOM:
143           y -= font->descent;
144           break;
145       case TA_BASELINE:
146           break;
147     }
148
149       /* Set the clip region */
150
151     if (flags & ETO_CLIPPED)
152     {
153         hRgnClip = dc->w.hClipRgn;
154         CLIPPING_IntersectClipRect( dc, rect.left, rect.top, rect.right,
155                                     rect.bottom, CLIP_INTERSECT|CLIP_KEEPRGN );
156     }
157
158       /* Draw the text background if necessary */
159
160     if (dc->w.backgroundMode != TRANSPARENT)
161     {
162           /* If rectangle is opaque and clipped, do nothing */
163         if (!(flags & ETO_CLIPPED) || !(flags & ETO_OPAQUE))
164         {
165               /* Only draw if rectangle is not opaque or if some */
166               /* text is outside the rectangle */
167             if (!(flags & ETO_OPAQUE) ||
168                 (x < rect.left) ||
169                 (x + info.width >= rect.right) ||
170                 (y-font->ascent < rect.top) ||
171                 (y+font->descent >= rect.bottom))
172             {
173                 XSetForeground( display, dc->u.x.gc, dc->w.backgroundPixel );
174                 XFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
175                                 dc->w.DCOrgX + x,
176                                 dc->w.DCOrgY + y - font->ascent,
177                                 info.width,
178                                 font->ascent + font->descent );
179             }
180         }
181     }
182     
183     /* Draw the text (count > 0 verified) */
184
185     XSetForeground( display, dc->u.x.gc, dc->w.textPixel );
186     if (!dc->w.charExtra && !dc->w.breakExtra && !lpDx)
187     {
188         XDrawString( display, dc->u.x.drawable, dc->u.x.gc, 
189                      dc->w.DCOrgX + x, dc->w.DCOrgY + y, str, count );
190     }
191     else  /* Now the fun begins... */
192     {
193         XTextItem *items, *pitem;
194         int delta;
195
196         /* allocate max items */
197
198         pitem = items = xmalloc( count * sizeof(XTextItem) );
199         delta = i = 0;
200         if( lpDx ) /* explicit character widths */
201         {
202             int extra = dc->wndExtX / 2;
203
204             while (i < count)
205             {
206                 /* initialize text item with accumulated delta */
207
208                 pitem->chars  = (char *)str + i;
209                 pitem->delta  = delta;
210                 pitem->nchars = 0;
211                 pitem->font   = None;
212                 delta = 0;
213
214                 /* add characters  to the same XTextItem until new delta
215                  * becomes  non-zero */
216
217                 do
218                 {
219                     delta += (lpDx[i] * dc->vportExtX + extra) / dc->wndExtX
220                                             - XTextWidth( font, str + i, 1);
221                     pitem->nchars++;
222                 } while ((++i < count) && !delta);
223                 pitem++;
224            }
225         }
226         else /* charExtra or breakExtra */
227         {
228             while (i < count)
229             {
230                 pitem->chars  = (char *)str + i;
231                 pitem->delta  = delta;
232                 pitem->nchars = 0;
233                 pitem->font   = None;
234                 delta = 0;
235
236                 do
237                 {
238                     delta += dc->w.charExtra;
239                     if (str[i] == (char)dfBreakChar) delta += dc->w.breakExtra;
240                     pitem->nchars++;
241                 } while ((++i < count) && !delta);
242                 pitem++;
243             } 
244         }
245
246         XDrawText( display, dc->u.x.drawable, dc->u.x.gc,
247                    dc->w.DCOrgX + x, dc->w.DCOrgY + y, items, pitem - items );
248         free( items );
249     }
250
251       /* Draw underline and strike-out if needed */
252
253     if (lfUnderline)
254     {
255         long linePos, lineWidth;       
256
257         if (!XGetFontProperty( font, XA_UNDERLINE_POSITION, &linePos ))
258             linePos = font->descent-1;
259         if (!XGetFontProperty( font, XA_UNDERLINE_THICKNESS, &lineWidth ))
260             lineWidth = 0;
261         else if (lineWidth == 1) lineWidth = 0;
262         XSetLineAttributes( display, dc->u.x.gc, lineWidth,
263                             LineSolid, CapRound, JoinBevel ); 
264         XDrawLine( display, dc->u.x.drawable, dc->u.x.gc,
265                    dc->w.DCOrgX + x, dc->w.DCOrgY + y + linePos,
266                    dc->w.DCOrgX + x + info.width, dc->w.DCOrgY + y + linePos );
267     }
268     if (lfStrikeOut)
269     {
270         long lineAscent, lineDescent;
271         if (!XGetFontProperty( font, XA_STRIKEOUT_ASCENT, &lineAscent ))
272             lineAscent = font->ascent / 3;
273         if (!XGetFontProperty( font, XA_STRIKEOUT_DESCENT, &lineDescent ))
274             lineDescent = -lineAscent;
275         XSetLineAttributes( display, dc->u.x.gc, lineAscent + lineDescent,
276                             LineSolid, CapRound, JoinBevel ); 
277         XDrawLine( display, dc->u.x.drawable, dc->u.x.gc,
278                    dc->w.DCOrgX + x, dc->w.DCOrgY + y - lineAscent,
279                    dc->w.DCOrgX + x + info.width, dc->w.DCOrgY + y - lineAscent );
280     }
281
282     if (flags & ETO_CLIPPED) SelectClipRgn32( dc->hSelf, hRgnClip );
283     return TRUE;
284 }